
/***************************************************************************
 *   Copyright (C) 1997 to 2004 by Jonathan Duddington                     *
 *   email: jonsd@users.sourceforge.net                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 3 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write see:                           *
 *               <http://www.gnu.org/licenses/>.                           *
 ***************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include "akbd.h"
#include "time.h"
#include "werr.h"
#include "wimp.h"
#include "wimpt.h"
#include "menu.h"
#include "template.h"
#include "dbox.h"
#include "win.h"
#include "event.h"
#include "bbc.h"
#include "flex.h"
#include "msgs.h"
#include "alarm.h"

#include "narc.h"
#include "hdrs.h"

#ifdef MemCheck
#include "MemCheck:Flex.h"
#endif


extern int os_version;
extern OPTIONS options;
extern char *path_choices;
extern wimp_menustr *wmenu_boxes_names;
extern menu menu_boxes_names;
extern wimp_menustr *wmenu_display;
extern BLIST boxlist;

extern FOLDREC cty_fr;
extern FOLDREC src_fr;
extern TEXTR *text_view;
extern TEXTR text_rec_temp;
extern TEXTR *text_data_record[N_TEXT_DATA];

extern int lists_line_height;
extern int lists_font_offset;
extern int y_screen_dim;
extern int scroll_bar_width;
extern int scroll_bar_height;

extern int search_case_sensitive_open;
extern int search_regex_flag_open;
extern char search_text_string_open[];

extern unsigned int list_backg_rgb;
extern unsigned int alist_backg_rgb;
extern DISPLAY display_tab[];
extern int colr_tab[];
extern BOX box_table[];
extern char box_password_ok[N_BOXES];
extern int null_event_map;
extern int transport_opts;
int pluto_run_options;
int pluto_run_options2;

extern int boxes_count[N_BOXES];
extern int boxes_unread_count[N_BOXES];
extern char desktop_font_name[];

extern menu blist_current_menu;
extern int blist_current_menu_line;
extern int blist_current_menu_blist;


#define COLR_TAB_GREY  8


#define CARD_INDEX_EXTRA  1000      /* extra room in index file */

static void open_list_window(wimp_openstr *o, FOLDREC *fr);
static void cardfile_remove2(FOLDREC *fr, CARD *cptr, int box);
void list_menu_proc(void *handle, char *hit);


int button_bar_height = 260;
int list_button_bar_height = 80;

static int copy_box;
static int  selects_level = 0;
static int  selects_count = 0;
static int  locked_count;
int  hide_level = STATUS_READ;    /* show articles less than this level */

CARD_HEADER card_header;
char menu_score_value[8];
TEXTR *text_view_iconised;

int main_index_sorted1 = 0;    /* 1= sorted by message_id */
int main_index_sorted2 = 0;
int main_index_sorted_break = 0;


extern int lock_expiry;       /* indicates that article expiry is in progress */
int lock_lists = 0;        /* disallow operations during debatch, sublist, reindexing */
FOLDREC *lock_lists_fr=NULL;   /* processing this article list */

static CARD *card_been_read = NULL;    /* waiting to be marked as read */
static int card_been_read_ref;
static list_escape_flag = 0;

/* list of the last articles which have been deleted */
#define N_CARDS_BINNED   10
static menu menu_binned = 0;
wimp_menustr *wmenu_binned = NULL;
static int cards_binned_ix = 0;
CARD *cards_binned[N_CARDS_BINNED];
static char cards_binned_box[N_CARDS_BINNED];
static char cards_binned_menulist[N_CARDS_BINNED];
static CARD *card_last_displayed = NULL;
static CARD *card_last_displayed2 = NULL;

int timezone_offset=0;

FOLDREC list_fr[N_LISTS];

static FOLDREC *current_fr;
int today_date;   /* date when Pluto started running */

menu  menu_list;
menu  menu_selection;


static int boxes_count_copy[N_BOXES];
static int boxes_unread_copy[N_BOXES];
static char box_any_read[N_BOXES+256];   /* Boxes + Sources */
int  fetch_count=3;

static int cardfile_autosave_timer=0;
int cardfile_changed[N_BOXES_EXTERN+1] = 0;
int dont_save_cardfile = 0;


MNEM_TAB months[] = {
	"   ", 0,
	"Jan", 1,
	"Feb", 2,
	"Mar", 3,
	"Apr", 4,
	"May", 5,
	"Jun", 6,
	"Jul", 7,
	"Aug", 8,
	"Sep", 9,
	"Oct", 10,
	"Nov", 11,
	"Dec", 12,
	"***", 13,
	"***", 14,
	NULL, 0
};


/* day of year for 1st of each month */
static short dofy[14] =  {0,1,32,60,91,121,152,182,213,244,274,305,335,366};
static short dofy2[14] = {0,1,32,61,92,122,153,183,214,245,275,306,336,367};




/* card_file used in cards.c and article.c */
/* entry 0 is mail cards file, 1 to 4 are for external boxes */
CARDFILE_REC cardfile[N_BOXES_EXTERN+1];





void clear_ixlist(FOLDREC *fr)
/****************************/
/* Clear the 'marked' bit throughout the index list */
{
	int  ix;
	unsigned int *ixlist;

	ixlist = fr->ixlist;
	if(fr->n_entries_tot < fr->n_entries)
		fr->n_entries_tot = fr->n_entries;

	for(ix=0; ix<fr->n_entries_tot; ix++)
	{
		ixlist[ix] &= ~IXLIST_MARKED;
	}
}   /* end of clear_ixlist */





int check_lock_lists(int flags)
/*****************************/
/* 1:  remaking index
   2:  debatching
   3:  updating
   4:  searching list to make subset
   5:  cardfile autosave
*/
{
	static char *msgs[] = {
		"","remaking index","loading articles","updating","searching","saving index",NULL,NULL,NULL
	};

	if((flags & 0xffff0000) == 0x10000)
	{
		if(flags & (1 << lock_lists))
		{
			/* flags 0-15 is a bitmap of allowed reasons */
			return(0);
		}
	}

	if(lock_lists != 0)
	{
		if(lock_lists == 5)
		{
			/* saving the index, just continue and it will be abandoned */
		}
		else
		{
			werr(0,"Pluto is busy %s, please wait",msgs[lock_lists&7]);
			return(lock_lists);
		}
	}
	return(0);
}   /* end of check_lock_lists */



int check_lock_lists_fr(int flags, FOLDREC *fr)
/*********************************************/
{
	if(fr != lock_lists_fr)
		return(0);

	return(check_lock_lists(flags));
}   /* end of check_lock_lists_fr */



void set_lock_lists(int reason, FOLDREC *fr)
/******************************************/
{
	lock_lists = reason;
	lock_lists_fr = fr;
}   /* end of set_lock_lists */


void clear_lock_lists(int reason)
/******************************/
{
	if(lock_lists == reason)
	{
		lock_lists_fr = NULL;
		lock_lists = 0;

		if(pluto_run_options)
		{
			pluto_wimp_message(pluto_run_options,pluto_run_options2,NULL);
			pluto_run_options = 0;
		}
	}
}   /* end of clear_lock_lists */



FOLDREC *search_cancel_flag=NULL;
int  search_cancel_reason = 0;



int search_cancel(FOLDREC *fr)
/****************************/
{
	if(search_cancel_flag == fr)
	{
		search_cancel_flag = NULL;
		search_cancel_reason = 1;   /* subset window closed */
		return(1);
	}
	return(0);
}   /* end of search_cancel */










char *get_cardfile2(FOLDREC *fr)
/*****************************/
{
	return(0);   /* ixlist addresses are now absolute */
}   /* end of get_cardfile */



CARD *cardfile_cptr(FOLDREC *fr, int ix)
/**************************************/
{
	if(ix >= fr->n_entries_max)
		return(NULL);

	return((CARD *)((fr->ixlist[ix] & IXLIST_MASK) << 2));
}   /* end of cardfile_ptr */




void init_cardex(CARD_EXPANDED *cardex)
/*************************************/
{
	/* initalise cardex fields */
	memset(cardex,0,sizeof(CARD_EXPANDED));
}   /* end of init_cardex */



void redraw_list_line_range(FOLDREC *fr,int line1,int line2)
/**********************************************************/
{
	wimp_redrawstr r;

	/* give workarea coordinates of line to be redrawn */
	r.w = fr->window;
	r.box.x0 = fr->workarea.box.x0;
	r.box.x1 = fr->workarea.box.x1;
	r.box.y0 = fr->workarea.box.y1 - ((line2+1)*lists_line_height);
	r.box.y1 = fr->workarea.box.y1 - (line1*lists_line_height)+2;
	wimp_force_redraw(&r);
}   /* end of redraw_list_line_range */





void redraw_list_oneline(FOLDREC *fr,int line)
/********************************************/
{
	redraw_list_line_range(fr,line,line);
}   /* end of redraw_list_oneline */





void redraw_list_lines(FOLDREC *fr,int linenum)
/*********************************************/
/* redraw from this line downwards */
{
	int  i;
	wimp_redrawstr r;

	linenum--;
	if(linenum < 0) linenum = 0;

	if(fr == NULL)
	{
		/* redraw all the article list windows */
		for(i=0; i<N_LISTS; i++)
		{
			redraw_list_lines(&list_fr[i],0);
		}
		return;
	}

	fr->redraw_flag = 0;

	/* give workarea coordinates of line to be redrawn */
	if(fr->window == NULL)
		return;

	r.w = fr->window;
	r.box.x0 = fr->workarea.box.x0;
	r.box.x1 = fr->workarea.box.x1;
	r.box.y0=  fr->workarea.box.y0 - lists_line_height;   /* ???? */
	r.box.y1=  fr->workarea.box.y1 - (linenum*lists_line_height);
	wimp_force_redraw(&r);
}   /* end of redraw_list_lines */






int encode_source_name(char *string)
/**********************************/
{
	return(category_lookup(&src_fr,string,0));
}


void encode_cats(CARD_EXPANDED *cardex, char *string)
/***************************************************/
{
}   /* end of encode_cats */


char *expand_cats(CARD_EXPANDED *cardex)
/**************************************/
{
	int  i;
	char *p;
	char *name;
	static char buf[128];

	p = buf;
	for(i=0; (i<cardex->n_cats) && (p-buf < 128-16); i++)
	{
		name = category_name(&cty_fr,cardex->cat_codes[i]);
		sprintf(p,"%s ",name);
		p+=(strlen(name)+1);
	}
	*p = 0;

	return(buf);
}   /* end of expand_cats */



char *expand_source(int source, int explan)
/*****************************************/
/* explain = 0  name
             1  name + comment
             2  comment (or name up to '@')
             3 as 2, but if no comment, strip "mail." from front of name
*/
{
	char *p;
	char *name;
	char *out;
	static char buf[150];

	name = category_name(&src_fr,source);
	out = buf;

	if(explan)
	{
		p = &src_fr.cat_chars[src_fr.cat_data[source].comment];
		if(p[0] <= ' ')
		{
			/* no comment field */
			strncpy(buf,name,sizeof(buf));
			if((explan==2) || (explan==3))
			{
				if((p = strchr(name,'@')) != NULL)
				{
					buf[p-name]=0;
				}

				if((explan==3) && (memcmp(buf,"mail.",5)==0))
					out = &buf[5];
			}
		}
		else
		{
			if(explan == 1)
				sprintf(buf,"%s  (%s)",name,p);
			else
				strcpy(buf,p);
		}
	}
	else
	{
		strncpy(buf,name,sizeof(buf));
	}
	buf[sizeof(buf)-1]=0;

	return(out);
}   /* end of expand_source */



int leap_year(int year)
/*********************/
{
	if((year % 4) != 0)
		return(0);

	if(((year % 100) == 0) && (year != 2000))
		return(0);

	return(1);
}   /* end of leap_year */




char *get_current_date_string(int *date_enc)
/******************************************/
{
	int  offset;
	os_regset regs;
	char date_buf[5];
	char timezone[12];
	char *offset_sign;
	char buf[32];
	static char date_string[44];

	regs.r[0] = 14;       /* OS_Word 14,3 */
	regs.r[1] = (int)date_buf;
	date_buf[0] = 3;      /* reason code */
	os_swi(0x07,&regs);

	regs.r[0] = 1;
	regs.r[1] = (int)date_buf;
	regs.r[2] = (int)buf;
	regs.r[3] = sizeof(buf);
	regs.r[4] = (int)"%W3, %DY %M3 %CE%YR %24:%MI:%SE";
	os_swi(0x4304b,&regs);   /* Territory_ConvertDateAndTime */

	/*   memcpy(&buf[8],months[month].mnem,3); */  /* Use English name for Month */

	regs.r[0] = 1;
	regs.r[1] = (int)date_buf;
	regs.r[2] = (int)timezone;
	regs.r[3] = sizeof(timezone);
	regs.r[4] = (int)"%TZ";
	os_swi(0x4304b,&regs);   /* Territory_ConvertDateAndTime */

	os_swi2r(0x43048,0,0,NULL,&offset);  /* Territory_ReadCurrentTimeZone */

	if(((strcmp(timezone,"GMT")==0) && (offset!=0))  ||
			((strcmp(timezone,"BST")==0) && (offset!=3600)))
	{
		timezone[0] = 0;
	}

	if(offset >= 0)
		offset_sign = "+";
	else
		offset_sign = "";

	if(timezone[0] == 0)
		sprintf(date_string,"%s %s%.4d",buf,offset_sign,offset/3600);
	else
		sprintf(date_string,"%s %s%.4d (%s)",buf,offset_sign,offset/3600,timezone);

	if(date_enc != NULL)
		*date_enc = encode_date(&date_string[4],0);

	return(date_string);
} /* end of get_current_date_string */





int encode_date(char *string, int control)
/****************************************/
/* Encode date from string in form 01 Feb 98
   control: bit 0:  0 = hh:mm:ss
                    1 = hhmm
            bit 1:  allow 01/02/98 format
*/
{
	int  i;
	int  c;
	int  day=0;
	int  month;
	int  year=0;
	int  hours=0;
	int  mins=0;
	int  secs=0;
	int  offset_sign=0;
	int  offset_value=0;
	short *dofy_data;

	char monthstr[32]= {0};
	char offset[32]= {0};
	char buf[32];

	static MNEM_TAB zone_names[] = {
		"BST", 1,
		"EST", -5,  "EDT", -4,
		"CST", -6,  "CDT", -5,
		"MST", -7,  "MDT", -6,
		"PST", -8,  "PDT", -7,
		NULL, 0
	};

	while(isalpha(*string))
	{
		/* skip day of week mnemonic */
		string++;
	}

	i=0;
	while((i < 31) && ((c = *string++) > '\n')) buf[i++] = c;
	buf[i] = 0;

	i = 0;
	if(control & 2)
	{
		i = sscanf(buf,"%d/%d/%d %d:%d:%d %s",&day,&month,&year,&hours,&mins,&secs,offset);
		if((month < 1) || (month > 12))
			month = 13;
	}

	if(i < 3)
	{
		month = -1;
		i = sscanf(buf,"%d%s%d %d:%d:%d %s",&day,monthstr,&year,&hours,&mins,&secs,offset);
	}
	if(i < 3)
		return(0);

	if((i==3) && (strchr(buf,'(')!=NULL))
		i = sscanf(buf,"%d%s%d (%d:%d:%d %s",&day,monthstr,&year,&hours,&mins,&secs,offset);


	if(month < 0)
	{
		strcpy_lc(&monthstr[1],&monthstr[1]);
		monthstr[0] = toupper(monthstr[0]);
		month = lookup_mnem(months,monthstr);
	}

	if(month <= 0)
	{
		/* we haven't recognised a month mnemonic, use today's date instead */
		return(get_today_date());
	}

	if(year < 300)
	{
		if(year >= 70)
			year += 1900;
		else
			year += 2000;
	}

	year = year - 1900;

	if(control & 1)
	{
		mins = hours % 100;
		hours = hours / 100;
	}
	mins = (mins * 2) / 5;
	hours = (hours * 24) + mins;   /* in 2.5 minute units */

	if(offset[0] != 0)
	{
		if(offset[0] == '+')
			offset_sign = -1;
		if(offset[0] == '-')
			offset_sign = 1;

		if(offset_sign != 0)
		{
			i = atoi(&offset[1]);
			offset_value = i / 50;
			if((i - offset_value*50) == 30)
				offset_value++;        // timezone has a half hour component
		}
		else
		{
			offset_sign = -1;

			offset_value = lookup_mnem(zone_names,offset)*2;
		}
		// offset_value is in half-hours
		hours += (offset_value * offset_sign * 12); /* in 2.5 minute units */
	}


	if(leap_year(year+1900))
		dofy_data = dofy2;
	else
		dofy_data = dofy;

	if(month == 0)
		month = 14;   /* not recognised */


	i = ((dofy_data[month] + day - 1) * 576) + hours;
	if(i < 576)
	{
		year--;
		i += ((dofy_data[12] + 30) * 576);
	}
	i = (year << 18) + i;

	return(i);
}   /* end of encode_date */




int decode_month(int date)
/************************/
/* Return year*16 + month */
{
	int month;
	int year;
	int  day;
	int  i;
	short *yeardays;

	date = (date + timezone_offset) & DATE_MASK;

	year = (date >> 18) + 1900;
	date &= 0x3ffff;
	day = date / 576;

	if(leap_year(year))
		yeardays = dofy2;
	else
		yeardays = dofy;

	for(i=1; i<14; i++)
	{
		if(day < yeardays[i])
			break;
	}
	month = i-1;
	return((year << 4) + month);
}   /* end of decode_month */



int date_subtract(int date1, int date2)
/*************************************/
/* date1 - date2 to accuracy of 1 day */
{
	int  y;

	y = (date1>>18) - (date2>>18);   /* years */

	return ((date1 & 0x3ffff) - (date2 & 0x3ffff) + y*(366*576));
}   /* end of date_subtract */



char *decode_date(int date,int timeflag)
/**************************************/
/* Decode internally stored date into form 01 Jan 96
   timeflag:
   bit 0  4 digit year
       1  include hrsmins
       2
       4  no year

   = 0x10  month and year
*/
{
	int month;
	int year;
	int  day;
	int  i;
	short *yeardays;
	int  hrsmins;
	int  hours;
	int  minutes;
	char *month_name;
	time_t ttime;
	struct tm *tmptr;
	struct tm t;
	static char buf[32];
	int  offset;

	if(date == 0)  return("");
	if(date == DATE_MASK) return("");

	if(timeflag & 2)
	{
		/* read timezone offset */
		os_swi2r(0x43048,0,0,NULL,&offset);  /* Territory_ReadCurrentTimeZone */
		timezone_offset = offset / 15000;    /* convert to * 10 mins */
	}

	date = (date + timezone_offset) & DATE_MASK;

	year = (date >> 18) + 1900;
	date &= 0x3ffff;
	day = date / 576;
	hrsmins = date - (day * 576);
	hours = hrsmins / 24;
	minutes = (hrsmins - (hours * 24)) / 2;
	minutes = minutes * 5;    /* !!!! divide then multiply rounds to 5 mins */

	if(leap_year(year))
		yeardays = dofy2;
	else
		yeardays = dofy;

	for(i=1; i<14; i++)
	{
		if(day < yeardays[i])
			break;
	}
	month = i-1;
	day = day - yeardays[month] + 1;


	/* Fix a problem with timezone offsets across a new year, this can be removed later */
	if(month==0)
	{
		day = 31;
		month = 12;
		year--;
	}

	if(month==13)
	{
		month = 1;
		year++;
	}

	if((timeflag & 4) == 0)
	{
		if((timeflag & 1) == 0)
			year = year % 100;

		month_name = months[month].mnem;

		if(timeflag == 0x10)
			sprintf(buf,"%s %02d",month_name,year);
		else if(timeflag & 8)
			sprintf(buf,"%02d %s",day,month_name);
		else if(timeflag & 2)
		{
			sprintf(buf,"%02d %s %02d %02d%02d",day, month_name, year,hours,minutes);
		}
		else
			sprintf(buf,"%02d %s %02d",day, month_name, year);
	}
	else
	{
		i = date % 576;
		t.tm_sec = 0;
		t.tm_min = ((i % 24) / 2) * 5;    /* !!!! divide then multiply rounds to 5 mins */
		t.tm_hour = i / 24;
		t.tm_mday = day;
		t.tm_mon = month-1;
		t.tm_year = year - 1900;
		t.tm_isdst = 0;

		ttime = mktime(&t);
		tmptr = localtime(&ttime);

		if(timeflag == 0xc)
			strftime(buf,31,"%a %d %b %y %H:%M",tmptr);
		else if(timeflag == 0xf)
			strftime(buf,31,"%a, %d %b %Y %H:%M:%S",tmptr);  /* Marcel */
		else
			strftime(buf,31,"%a, %d %b %Y %H:%M",tmptr);
	}
	return(buf);
}   /* end of decode_date */



int date_addition(int date, int days)
/***********************************/
{
	int date1;
	int year;
	int year_days;

	year = date >> 18;

	date1 = (date & 0x3ffff) / 576;
	date1 += days;

	for(;;)
	{
		if(leap_year(year+1900))
			year_days = 366;
		else
			year_days = 365;

		if(date1 < year_days)
			break;

		date1 -= year_days;
		year++;
	}
	return((year<<18) + date1*576);
}   /* end of date_addition */



int get_today_date()
/******************/
{
	time_t timer;
	struct tm* t;
	char buf[16];

	timer = time(NULL);
	t = localtime(&timer);

	sprintf(buf,"%02d %s %02d",t->tm_mday,months[t->tm_mon+1].mnem,t->tm_year+1900);
	return(encode_date(buf,1));
}   /* end of get_today_date */


int get_today_date2(char *out)
/***************************/
{
	time_t timer;
	struct tm* t;

	timer = time(NULL);
	t = localtime(&timer);

	sprintf(out,"%02d %s %02d %.2d%.2d",t->tm_mday,months[t->tm_mon+1].mnem,t->tm_year % 100,t->tm_hour,t->tm_min);
	return(encode_date(out,1));
}   /* end of get_today_date */




int interpret_date(char *string)
/********************************/
/* Convert mail or news date into a standard form */
/* ascii form is: Tue, 13 Aug 96 12:44:16 or  Thu, 08 Aug 1996 20:07:29 BST */
{
	int  i;

	/* skip to first digit */
	while((!isdigit(*string)) && (*string > '\n'))
		string++;

	i = encode_date(string,0);
	return(i);
}   /* end of interpret_date */


void list_set_title(FOLDREC *fr)
/******************************/
/* Set title to source name if a source group is open */
{
	CARD *cptr;
	char buf[150];

	if((fr->open_levels > 0) && (fr->display->level[0].sort_field == CS_SOURCE))
	{
		cptr = cardfile_cptr(fr,fr->open_ref[0]);
		sprintf(buf,"%s:  %s       ",fr->name,expand_source(cptr->source,0));
		buf[80]=0;
		win_settitle(fr->window_buttons,buf);
	}
	else
	{
		win_settitle(fr->window_buttons,fr->name);
	}
}   /* end of list_set_title */





static void set_list_extent(FOLDREC *fr, int open, int linenum)
/*************************************************************/
/*  open:  bit 0 = open
           bit 1 = redraw
           bit 2 = open for first time
*/

{
	int size;
	wimp_wstate wstate;
	wimp_redrawstr w_workarea;


	size = fold_n_lines(fr,0) * lists_line_height;

	/* list is smaller, redraw the larger size */
	/*
	   if((fr->workarea.box.y1 - size) > fr->workarea.box.y0)
	   {
	      redraw_list_lines(fr,0);
	   }
	*/
	redraw_list_lines(fr,linenum);


	fr->workarea.box.y1 = 0;
	fr->workarea.box.y0 = fr->workarea.box.y1 - size;
	fr->workarea.box.x0 = 0;
	fr->workarea.box.x1 = fr->display->window_width;
	fr->workarea.w = fr->window;
	wimp_set_extent(&fr->workarea);

	memcpy(&w_workarea,&fr->workarea,sizeof(w_workarea));
	w_workarea.box.y0 = -(size  + list_button_bar_height);
	w_workarea.box.x1 = fr->display->window_width+scroll_bar_width;
	w_workarea.w = fr->window_buttons;
	wimp_set_extent(&w_workarea);

	if(open)
	{
		wimp_get_wind_state(fr->window,&wstate);

		if(open)
		{
			wstate.o.box.y0 = wstate.o.box.y1 - size;

			if(options.restrict_height==2)
				wstate.o.box.y0 = options.position_y0[3];

			if((options.restrict_height==1) || preserve_iconbar())
				wstate.o.box.y0 = 160;  /* don't cover icon bar */
		}

		if(open & 2)
			redraw_list_lines(fr,linenum);

		open_list_window(&wstate.o,fr);
	}
}   /* end of set_list_extent */



void list_open_window(FOLDREC *fr)
/********************************/
{
	set_list_extent(fr,5, 0);
}   /* end of list_open_window */



void list_open_window_all(int open)
/*********************************/
{
	int  i;
	FOLDREC *fr;
	int  wimp_colr;

	if(options.colours[COLR_ALIST] == 0)
		wimp_colr = 0;
	else
		wimp_colr = 255;   /* transparent */

	for(i=1; i<N_LISTS; i++)
	{
		fr = &list_fr[i];
		if(fr->window != NULL)
		{
			set_list_extent(fr,open, 0);
		}
	}
}   /* end of list_open_window_all */



void list_open_at(FOLDREC *fr, int card, int card_viewer)
/*******************************************************/
/* Open and scroll to the specified record
     card = article at cursor
     card_viewer = article in article viewer */
{
	int  ix;
	int  c;
	unsigned int *ixlist;
	int  linenum;
	int  y;
	int vector[N_FOLD_LEVELS];

	ixlist = fr->ixlist;

	for(ix=0; ix<fr->n_entries; ix++)
	{
		if(card_viewer == (c = ((ixlist[ix] & IXLIST_MASK)<< 2)))
		{
			fr->open_article_ref = ix;
			fr->open_article_source = ((CARD *)card_viewer)->source;

			if(card == 0)
				break;
			card_viewer = 0;
		}

		if(card == c)
		{
			fold_ref_to_vector(fr,ix,fr->display_levels,vector);
			fold_open_at_vector(fr,vector,fr->display_levels-1);

			if((fr->display->threading) && (fr->threading_done==0))
			{
				thread_sort(fr,fr->open_ref[0],0);

				fold_ref_to_vector(fr,ix,fr->display_levels,vector);
				fold_open_at_vector(fr,vector,fr->display_levels-1);
			}

			fr->cursor_ref = ix;
			ixlist[ix] = ixlist[ix] | ((fr->display_levels+1) << IXSEL);  /* select this line */

			linenum = fold_vector_to_line(fr,vector,fr->open_levels);
			set_list_extent(fr,1, 0);

			y = -(linenum+3) * lists_line_height;
			scroll_to_y(fr->window,0);
			scroll_to_y(fr->window,y);

			if(card_viewer == 0)
				break;
			card = 0;
		}
	}
}   /* end of list_open_at */




void list_close_all(FOLDREC *fr, int show)
/****************************************/
{
	int  level;

	fr->cursor_ref = 0;

	memset(fr->n_open,0,sizeof(fr->n_open));

	if(fr->n_entries == 0)
	{
		fr->open_levels = 0;
	}
	else
	{
		for(level=0; level<=fr->display_levels; level++)
		{
			fr->n_open[level] = fold_count_level(fr,0,level-1);
			fr->open_levels = level;

			if(fr->n_open[level] > 1)
				break;
		}
	}

	if(fr->display->threading)
	{
		if((fr->open_levels >= 1) && (fr->threading_done==0))
		{
			thread_sort(fr,fr->open_ref[0],0);
			fold_open_at_line(fr,fr->open_levels-1);
		}
	}
	set_list_extent(fr,show, 0);
}   /* end of list_close_all */




void list_close_level(FOLDREC *fr)
/********************************/
/* Close article list at the current level */
{
	if(fr->open_levels <= 0)
	{
		beep();
		return;
	}
	redraw_list_lines(fr,0);
	fold_close_level(fr,fr->open_levels-1);
	set_list_extent(fr,1,0);
}   /* end of list_close_level */




void list_delete_window(FOLDREC *fr)
/**********************************/
{
	if(search_cancel(fr))
		user_message("Search cancelled");
	else
	{
		if(lock_lists_fr == fr)
		{
			/* don't close the article list if we're processing it */
			if(check_lock_lists(4) != 0)
				return;   /* searching list, don't allow closing an article list */
		}
	}

	if(fr->box >= 0)
	{
		/* don't allow further access to this box after we have closed it */
		if((box_table[fr->box].flags & BOX_DONT_HOLD_PWORD) && (box_table[fr->box].box_password != 0))
			box_password_ok[fr->box] = 0;
	}

	if(fr->window != NULL)
	{
		win_register_event_handler(fr->window,NULL,NULL);
		wimp_delete_wind(fr->window);
		dbox_dispose(&fr->dbox_list);
		fr->window = NULL;
	}

	if(fr->ixlist != NULL)
	{
		free(fr->ixlist);
		fr->ixlist = NULL;
	}

	fr->name[0] = 0;
}   /* end of list_delete_window */



void list_sort(FOLDREC *fr, int show)
/********************************/
/* Show = 1, = 2 open at same record */
{
	int  card = -1;
	int  card_viewer;   /* article in article viewer */

	visdelay2_begin();

	if(show == 2)
	{
		if((fr->open_levels == fr->display_levels) &&
				(fr->ixlist[fr->cursor_ref] & IXLIST_SELECTED))
		{
			card = (fr->ixlist[fr->cursor_ref] & IXLIST_MASK) << 2;
		}
	}

	card_viewer = (fr->ixlist[fr->open_article_ref] & IXLIST_MASK) << 2;

	sort_index_list(fr, 0);
	list_close_all(fr,0);
	list_set_title(fr);

	if(card >= 0)
	{
		list_open_at(fr,card, card_viewer);   /* open at this position */
	}

	dbox_setfield(fr->dbox_list,7,fr->display->name);

	if(show)
		list_open_window(fr);

	visdelay2_end();
}   /* end of list_sort */




void list_fade_icons(FOLDREC *fr,int fade_flag)
/*********************************************/
{
	int  i;
	for(i=1; i<6; i++)
	{
		/* fade these icons */
		wimp_set_icon_state(fr->window_buttons,i,fade_flag,wimp_INOSELECT);  /* fade Post button */
	}
}   /* end of list_fade_icons */




void list_set_select(FOLDREC *fr, int ix, int select_bits)
/********************************************************/
{
	int  prev;
	int  any_selected = 0;

	if(fr->n_selected > 0)
		any_selected = 1;

	if(ix < 0)
	{
		fr->n_selected = 0;
	}
	else
	{
		prev = fr->ixlist[ix] & IXLIST_SELECTED;
		fr->ixlist[ix] = (fr->ixlist[ix] & ~IXLIST_SELECTED) | select_bits;

		if(prev != select_bits)
		{
			if(select_bits)
				fr->n_selected++;
			else
				fr->n_selected--;
		}
	}

	if(any_selected)
	{
		if(fr->n_selected <= 0)
		{
			list_fade_icons(fr,wimp_INOSELECT);
		}
	}
	else
	{
		if(fr->n_selected > 0)
		{
			list_fade_icons(fr,0);
		}
	}
}   /* end of list_add_select */





static void card_index_change1(FOLDREC *fr, CARD *cptr,CARD *new_cptr)
/********************************************************************/
/* Remove a card from the index (can be in any order),
   or replaces it by a new value */
{
	int  i;
	int  ix;
	int  level;
	int  lev;
	int  n_entries;
	unsigned int  *card_index;

	if(fr == &list_fr[0])
		fr->n_entries_tot = fr->n_entries;

	if(fr->n_entries_tot < fr->n_entries)
	{
		fr->n_entries = fr->n_entries_tot;
	}

	n_entries = fr->n_entries_tot;

	card_index = fr->ixlist;


	if(cptr == NULL)
		return;

	/* find the card in the index */
	for(i=0; i<n_entries; i++)
	{
		if(((card_index[i] & IXLIST_MASK) << 2)== (int)cptr)
			break;
	}

	if(i == n_entries)
		return;   /* not found */

	ix = i;

	if(new_cptr == NULL)
	{
		if((fr->open_article_ref >= ix) && (fr->open_article_ref >= 0))
			fr->open_article_ref--;

		/* adjust open count */
		list_set_select(fr,ix,0);   /* adjust count of selected items */
		fold_remove_ref(fr,ix);

		/* move all subsequent entries up one place */
		n_entries -= 1;

		level = (card_index[ix] >> IXLEV);
		if(level > fr->display_levels)
			level = fr->display_levels;

		for( ; i<n_entries; i++)
		{
			card_index[i] = card_index[i+1];
		}
		if(ix < fr->n_entries)
			fr->n_entries--;

		lev = card_index[ix] >> IXLEV;
		if(lev > fr->display_levels)
			lev = fr->display_levels;

		if((ix < n_entries) && (lev > level))
		{
			/* carry level down to next index */
			card_index[ix] = (card_index[ix] & ~IXLIST_LEVEL) + (level << IXLEV);
		}

		fr->redraw_flag = 1;
	}
	else
	{
		card_index[ix] = (card_index[ix] & ~IXLIST_MASK) + (((unsigned int)new_cptr) >> 2);
	}

	fr->n_entries_tot = n_entries;

}   /* end of card_index_change1 */



static void card_index_change(CARD *cptr,CARD *new_cptr, FOLDREC *fr)
/*******************************************************************/
/* Remove a card from the all the indices (index can be in any order),
   or replaces it by a new value
   fr= don't remove from this list */
{
	int list;

	if(new_cptr != NULL)
		list = 0;     /* change cptr, do all lists */
	else
		list = 1;     /* remove card from all lists except list[0] */

	for(; list<N_LISTS; list++)
	{
		if(&list_fr[list] == fr)
			continue;

		if((list==0) || (list_fr[list].window != NULL))
			card_index_change1(&list_fr[list],cptr,new_cptr);
	}
}   /* end of card_index_change */



void card_add_binned_list(CARD *cptr)
/***********************************/
{
	cards_binned[cards_binned_ix] = cptr;
	cards_binned_box[cards_binned_ix] = cptr->date_box >> 26;
	cards_binned_ix++;
	if(cards_binned_ix >= N_CARDS_BINNED)
		cards_binned_ix = 0;
}   /* end of card_add_binned_list */



void card_binned_selected(int *hits)
/**********************************/
{
	CARD *cptr;
	int  ix;

	if(menu_binned != 0)
	{
		menu_dispose(&menu_binned,0);
		menu_binned = 0;
	}

	ix = cards_binned_menulist[hits[0]];
	if(ix == 255)
		return;

	cptr = cards_binned[ix];
	if(cptr == NULL)
	{
		beep();
		return;
	}

	cardfile_remove2(current_fr,cptr,cards_binned_box[ix]);
	article_view2(current_fr, -1, cptr);
}   /* end of card_binned_selected */



void card_make_binned_menu(FOLDREC *fr)
/*************************************/
{
	int  ix;
	CARD *cptr;
	int  count=0;
	int  i;
	char *p;
	int  c;
	char buf[256];

	if(menu_binned != 0)
		menu_dispose(&menu_binned,0);

	menu_binned = menu_new("Undelete","");
	current_fr = fr;

	cards_binned_menulist[0] = 255;
	ix = cards_binned_ix;
	for(;;)
	{
		ix--;
		if(ix < 0)
			ix = N_CARDS_BINNED-1;

		if((cptr = cards_binned[ix]) == NULL)
			break;

		if(((cptr->date_box >> 26) == BOX_BIN) &&
				(box_password_check(cards_binned_box[ix],0)==1))
		{
			sprintf(buf,"%s",&cptr->data[cptr->d_title]);
			buf[24] = 0;
			i = strlen(buf);
			memcpy(&buf[i],"  (",3);
			sprintf(&buf[i+3],"%s",&cptr->data[cptr->d_author]);
			buf[36] = 0;

			/* replace punctuation to avoid programs with menu */
			p = buf;
			while((c = *p) != 0)
			{
				if(strchr(",!~|>",c)!=NULL)
					*p = ' ';
				p++;
			}
			menu_extend(menu_binned,buf);
			cards_binned_menulist[count++] = ix;
		}

		if(ix == cards_binned_ix)
			break;
	}
	if(count==0)
	{
		menu_extend(menu_binned,"(none)");
	}
	wmenu_binned = menu_syshandle(menu_binned);
}   /* end of card_make_binned_menu */





static void box_move_count(int box1, int box2, int status)
/********************************************************/
/* Adjust counts when moving from box1 to box2 */
{
	if(box1 >= 0)
		boxes_count_copy[box1]--;
	if(box2 >= 0)
		boxes_count_copy[box2]++;

	if((status & STATUS_MASK) <= STATUS_UNREAD)
	{
		if(box1 >= 0)
			boxes_unread_copy[box1]--;
		if(box2 >= 0)
			boxes_unread_copy[box2]++;
	}
}   /* end of box_move_count */






static int list_delete_block(FOLDREC *fr, int *reference, int level, int force, int box, int copy, int *progress, int total)
/************************************************************************************************/
/* Delete all the articles below the specified level.
   Return the (moved) index of the following item.
   Or, if box != BOX_BIN, change the box of the articles
   copy=0 don't copy,  1=copy,  2=count only
   progress/total is fraction of total operation prformed */
{
	int  ix;
	int  ref;
	unsigned int *ixlist;
	int  n_entries;
	int  n_delete;     /* number of items to delete */
	int  start;
	int  end;
	int  n_block;
	int  level_field;     /* level field of specified item */
	int  select_level;
	int  min_status;
	int  n_kept;
	int  n_moved;
	int  status;
	int  box1;
	int  lev;
	int  external_move;
	CARD *cptr;
	CARD_EXPANDED cardexp;

	static char *err_abort = "Move articles aborted";

	memset(&cardexp,0,sizeof(cardexp));

	ref = *reference;

	ixlist = fr->ixlist;
	n_entries = fr->n_entries;

	n_block=0;
	start=0;
	end=n_entries;

	level_field = ixlist[ref] >> IXLEV;

	select_level = ixlist[ref] & IXLIST_SELECTED;

	/* count the specified level */
	ix = ref + 1;
	while(ix < n_entries)
	{
		lev = ixlist[ix] >> IXLEV;
		if(lev > fr->display_levels)
			lev = fr->display_levels;
		if(lev <= level)
			break;
		ix++;
	}

	n_delete = ix - ref;

	if(copy==2)
		return(n_delete);   /* just counting */

	/* find status of items */
	min_status = STATUS_READ;
	if((fr->hide_read) && (ref != fr->open_ref[fr->open_levels-1]))
	{
		for(ix=ref; ix<(ref+n_delete); ix++)
		{
			cptr = (CARD *)((ixlist[ix] & IXLIST_MASK) << 2);
			if((cptr->status & STATUS_MASK) < min_status)
				min_status = cptr->status & STATUS_MASK;
		}
	}

	/* delete cards which are referenced from these items */
	n_kept = 0;
	n_moved = 0;
	for(ix=ref; ix<(ref+n_delete); ix++)
	{
		if(total > 0)
			visdelay2_percent(((*progress)++ * 100)/total);

		if(list_escape_flag)
		{
			break;
		}

		cptr = (CARD *)((ixlist[ix] & IXLIST_MASK) << 2);
		if(((cptr->date_box >> 26) == box) && (box != BOX_BIN))
		{
			n_kept++;
			continue;   /* already in the required box */
		}

		box1 = cptr->date_box >> 26;   /* original box */

		if(box != BOX_BIN)
		{
			external_move = 0;

			if(box >= BOX_EXTERN)
				external_move = 1;

			set_lock_lists(3,fr);
			call_event_process(0);
			clear_lock_lists(3);

			if(box1 == BOX_BIN)
			{
				article_delete(fr,cptr,1,1);   /* resurrect article from bin */
			}
			else
			{
				if(box1 >= BOX_EXTERN)
					external_move = 1;
			}

			if(external_move)
			{
				if(disc_free_space(cardfile[box_to_cf(box)].path) < 2000)
				{
					/* less than 2 MBytes free */
					werr(0,"Disc nearly full, abandon Move");
					list_escape_flag = 1;
					continue;
				}

				unpack_card(cptr,&cardexp);
				if(article_read(&text_rec_temp,cardexp.addr,cardexp.docbox,cardexp.text_offset,cardexp.text_length,0,0,0) < 0)
					continue;

				cardexp.docbox = box;
				cardexp.text_anchor = &text_rec_temp.text_base;
				cardexp.text_start = 0;
				cardexp.text_length = text_rec_temp.text_length;

				/* copy to the external article file for this box */
				if((article_open_shortest(box,cardexp.text_length,-1)==NULL) ||
						(cardfile_add(&cardexp)==NULL))
				{
					werr(0,err_abort);
					list_escape_flag = 1;
					continue;
				}

				box_move_count(box1,box,cptr->status);

				if(copy == 0)
				{
					/* remove original to bin */
					card_index_change(cptr,NULL,fr);   /* remove from all lists */
					article_delete(fr,cptr,1,6);  /* update article file -> BIN */
					if(box_to_cf(box1) > 0)
					{
						/* hide deleted article if it's in an external box, not in the real BIN box */
						cptr->status = cptr->status & ~STATUS_MASK2 | STATUS_HIDDEN;
					}

				}
				else
				{
					box_move_count(-1,box1,cptr->status);
					n_kept++;
				}
			}
			else
			{
				/* set the new box number */
				cptr->date_box = (cptr->date_box & ~BOX_MASK) + (box << 26);
				box_move_count(box1,box,cptr->status);

				if(copy && (box1 < BOX_BIN))
				{
					unpack_card(cptr,&cardexp);
					if(article_read(&text_rec_temp,cardexp.addr,cardexp.docbox,cardexp.text_offset,cardexp.text_length,0,0,0) < 0)
						continue;

					cardexp.docbox = box1;
					cardexp.text_anchor = &text_rec_temp.text_base;
					cardexp.text_start = 0;
					cardexp.text_length = text_rec_temp.text_length;

					if((article_open_shortest(-1,cardexp.text_length,-1)==NULL) ||
							(cardfile_add(&cardexp)==NULL))
					{
						werr(0,err_abort);
						list_escape_flag = 1;
						continue;
					}

					box_move_count(-1,box1,cptr->status);

					n_kept++;
				}

				/* does the new box require strip headers ? */
				if(box_table[box].flags & BOX_STRIP_HEADERS)
					article_delete_internet_header(fr,cptr,1);
				else
					article_update(fr,cptr,NULL,0,0);  /* update article file */
			}

			n_moved++;
			continue;   /* next article */
		}

		/* else destination box is BIN */

		status = cptr->status & STATUS_MASK;
		if((min_status <= STATUS_UNREAD) && (status > STATUS_UNREAD) && (status < STATUS_READ))
		{
			/* there are read items in this thread, and read items are hidden,
			   do not delete read items */
			ixlist[ref+n_kept++] = ixlist[ix];   /* move up */
			continue;
		}

		if(!force && (status > STATUS_UNREAD) && (status < STATUS_READ))
		{
			locked_count++;
			ixlist[ref+n_kept++] = ixlist[ix];   /* move up */
			continue;   /* locked against deletion */
		}

		if((box1) >= BOX_EXTERN)
		{
			/* already in BIN, or delete fom an external box */
			box_move_count(box1,-1,cptr->status);
			cptr->status = cptr->status & ~STATUS_MASK2 | STATUS_HIDDEN;
		}
		else
		{
			box_move_count(box1,box,cptr->status);
			card_add_binned_list(cptr);
		}

		set_lock_lists(3,fr);
		call_event_process(0);
		clear_lock_lists(3);

		card_index_change(cptr,NULL,fr);   /* remove from all lists */
		article_delete(fr,cptr,0,4);       /* and box -> BIN */

		n_moved++;
	}
	article_file_close();

	/* remove from this list, move following items up */
//   n_delete -= n_kept;
	n_kept = n_delete - n_moved;
	n_delete = n_moved;

	if(n_delete > 0)
	{
		for(ix=ref+n_kept; ix<(n_entries - n_delete); ix++)
		{
			ixlist[ix] = ixlist[ix+n_delete];
		}
		fr->n_entries -= n_delete;
		fr->n_entries_tot -= n_delete;

		/* adjust reference of open article, for Next operations */
		if(fr->open_article_ref > (ref+n_kept))
			fr->open_article_ref -= n_delete;
	}

	/* conserve level information from specified item */
	if((level_field < fr->display_levels) &&
			((ixlist[ref] >> IXLEV) > level_field))
	{
		if(n_kept > 0)
		{
			/* keep this group selected */
			ixlist[ref] = (ixlist[ref] & ~(IXLIST_LEVEL | IXLIST_SELECTED)) + (level_field << IXLEV) + select_level;
		}
		else
			ixlist[ref] = (ixlist[ref] & ~IXLIST_LEVEL) + (level_field << IXLEV);
	}

	*reference = ref + n_kept;

	return(ref - n_delete);
}   /* end of list_delete_block */



/*************************************************************************/
/*   Load and Save Card Files */
/*************************************************************************/










void save_box_any_read()
/**********************/
{
	FILE *f;
	char fname[128];

	sprintf(fname,"%s.boxes_read",path_choices);
	f = fopen(fname,"w");
	if(f != NULL)
	{
		fwrite(box_any_read,1,sizeof(box_any_read),f);
		fwrite(&fetch_count,1,sizeof(fetch_count),f);
		fclose(f);
	}
}   /* end of save_box_any_read */




void load_box_any_read()
/**********************/
{
	FILE *f;
	char fname[128];

	sprintf(fname,"%s.boxes_read",path_choices);
	f = fopen(fname,"r");
	if(f != NULL)
	{
		fread(box_any_read,1,sizeof(box_any_read),f);
		fread(&fetch_count,1,sizeof(fetch_count),f);
		fclose(f);
	}
	else
	{
		/* can't read file, assume all boxes contain New
		   articles which have been read */
		memset(box_any_read,3,sizeof(box_any_read));
	}
}   /* end of load_box_any_read */





void cardfile_new_to_unread(int control)
/**********************************/
/* Change the status fields of all cards
    control: bit 0: mail,  bit 1: news
*/
{
	int  ix;
	unsigned int *ixlist;
	int  n_entries;
	CARD *cptr;
	int  found;
	int  action;

	action = options.new_to_unread;
	found = 0;
	if(control == -1)
	{
		action = 2;   /* all */
	}

	if(action != 3)
	{
		/* have any messages, anywhere been read */
		for(ix=0; ix<N_BOXES; ix++)
		{
			if(box_any_read[ix] & control)
				found = 1;
		}
	}

	if(found)
	{
		ixlist = list_fr[0].ixlist;
		n_entries = list_fr[0].n_entries;
		for(ix=0; ix<n_entries; ix++)
		{
			cptr = (CARD *)((ixlist[ix] & IXLIST_MASK) << 2);
			if((cptr->status & STATUS_MASK) <= STATUS_NEW)
			{

				if(cptr->status & STATUS_BIT_NEWS)
				{
					if((control & 2) == 0)
						continue;
				}
				else
				{
					if((control & 1) == 0)
						continue;
				}

				if(action == 0)
				{
					if(box_any_read[cptr->date_box >> 26] & control)
						cptr->status += (STATUS_UNREAD-STATUS_NEW);
				}
				else if(action == 1)
				{
					if(box_any_read[cptr->source + N_BOXES] & control)
						cptr->status += (STATUS_UNREAD-STATUS_NEW);
				}
				else if(action == 2)
				{
					cptr->status += (STATUS_UNREAD-STATUS_NEW);
				}
			}
		}
	}

	/* clear any-read flag for all boxes */
	for(ix=0; ix<sizeof(box_any_read); ix++)
	{
		box_any_read[ix] &= ~control;
	}
}   /* end of cardfile_new_to_unread */





static void add_cardfile_to_index(char *cardfile_base, int n_entries, int cf)
/***************************************************************************/
/* Make a list of the addresses of all the cards in the card file.
  If cf>0, indicating an external box, its records are added to the index */
{
	CARD *cp;
	int  card_num;
	int  length;
	int  ix;
	int  box;
	unsigned int *ixlist;
	FOLDREC *fr;

	cp = (CARD *)cardfile_base;
	fr = &list_fr[0];   /* ??? */

	main_index_sorted2 = 0;  /* top part of index no longer sorted by msgid */
	if(cf==0)
	{
		/* loaded the main index */
		main_index_sorted1 = 0;  /* none of the index now sorted my msgid */
	}

	ix = fr->n_entries + n_entries;

	if(fr->n_entries_max <= ix)
	{
		ixlist = realloc(fr->ixlist,(ix + CARD_INDEX_EXTRA)*sizeof(int));

		if(ixlist == NULL)
		{
			malloc_err(1);
			return;
		}
		fr->ixlist = ixlist;
		fr->n_entries_max = ix + CARD_INDEX_EXTRA;
	}

	ix = fr->n_entries;
	box = cf_to_box(cf);
	for(card_num = 0; card_num < n_entries; card_num++)
	{
		length = card_size(cp);

		/* date = -1 indicates card has been deleted */
		if((cp->date_box >> 26) != BOX_DELETED)
		{
			fr->ixlist[ix++] = ((unsigned int)cp) >> 2;
		}

		if(cf > 0)
		{
			/* use the external box number */
			cp->date_box = (cp->date_box & ~BOX_MASK) | (box << 26);
		}

		cp = (CARD *)(length + (char *)cp);
	}

	fr->n_entries = ix;
	fr->n_entries_tot = ix;
}   /* end of add_cardfile_to_index */





int cardfile_load(int cf)
/***********************/
/* Load the card file for the specified database */
{
	FILE *f_in;
	int  length;
	int  i;
	CARD_HEADER card_hdr2;
	char fname[200];
	char fname2[200];
	char buf[80];


	sprintf(fname,"%s.cards",cardfile[cf].path);
	sprintf(fname2,"%s.headers",cardfile[cf].path);

	length = get_filelength(fname);
	length = length - sizeof(card_hdr2);

	f_in = fopen_werr(fname,"r",NULL);
	if(f_in == NULL)
		return(-1);

	/* delete the "headers" file while we load "cards" so a crash will
	   leave "headers missing, so the next time Pluto runs it will
	   rebuild the index */
	article_read_file_headers(cf);
	remove(fname2);

	if(cardfile[cf].base != NULL)
		free(cardfile[cf].base);

	cardfile[cf].base = malloc(length);
	cardfile[cf].length = length;

	i = (int)cardfile[cf].base;
	if(cardfile[cf].base == NULL)
	{
		sprintf(buf,"to load %s",fname);
		malloc_err_string(2,buf);
		if(f_in != NULL)  fclose(f_in);

		article_save_file_headers(cf);
		return(-1);
	}

	if((i + length) > 0x01fff000)
	{
		/* only 23+2 bits in index files to point to cardfile entries */
		werr(0,"Index files too big");
		if(f_in != NULL)  fclose(f_in);
		article_save_file_headers(cf);
		return(-1);
	}

	fread(&card_hdr2,1,sizeof(card_hdr2),f_in);
	fread(cardfile[cf].base,length,1,f_in);
	fclose(f_in);

	if((card_hdr2.n_cards > 0) && ((length / card_hdr2.n_cards) < 60))
	{
		remove(fname);
		werr(0,"Index file %s.Cards was trucated",cardfile[cf].path);
		return(-1);
	}

	cardfile_changed[cf] = 0;
	cardfile[cf].n_entries = card_hdr2.n_cards;

	if(cf==0)
	{
		memcpy(&card_header,&card_hdr2,sizeof(card_header));  /* remember timestamp for expiry */
	}

	add_cardfile_to_index(cardfile[cf].base,cardfile[cf].n_entries,cf);

	/* restore the headers file now that we've completed reading cards */
	article_save_file_headers(cf);

	return(0);
}   /* end of cardfile_load */





void cardfile_autosave_set(int cf)
/********************************/
{
	cardfile_changed[cf] = 1;
	cardfile_autosave_timer = options.index_autosave;   /* in minutes */
}



void cardfile_autosave_abandon()
/******************************/
{
	char path[200];

	cardfile_autosave_set(0);  /* save it again later */

	/* Delete file to ensure index is known to be out of date */
	sprintf(path,"%s.headers",pluto_articles);
	remove(path);
}   /* end of cardfile_autosave_abandon */





int cardfile_save(int cf, int multiprocess)
/*****************************************/
/* Save the card file. returns number of entries
   multiprocess, bit  = 0=multiprocess, 1=and don't report error
      bit 2= only timestamp */
{
	FILE *f_out=NULL;
	int  card_num;
	int  n_cards;
	int  length;
	int  box;
	int  error=0;
	CARD *cp;
	FOLDREC *fr;
	char fname[256];

	article_save_file_headers(cf);

	fr = &list_fr[0];

	if(multiprocess & 1)
	{
		if(check_lock_lists(0xffff))
			return(0);
	}

	save_box_any_read();

	sprintf(fname,"%s.cards",cardfile[cf].path);
	if(multiprocess & 4)
	{
		// only set the timestamp, so that we don't rebuilt it
		// next time we run Pluto
		os_swi2(0x08, 9, (int)fname);
		return(0);
	}

	/* try to detect read-only access by deleting the old cards file.
	   we need to do this because failed on opening for write access
	   still updates the time-stamp */

	if(get_filelength(fname) > 0)
	{
		if(remove(fname) != 0)
		{
			/* can't remove the old cards file - must be locked */
			error = 1;
		}
	}
	if(error == 0)
		f_out = fopen(fname,"w+");

	if(f_out == NULL)
	{
		/* don't report error on autosave, may be readonly file */
		if((multiprocess & 0x2) == 0)
		{
			if(cf==0)
				werr(0,"Can't update Pluto's index file: %s",fname);
			else
				werr(0,"Can't update index file for box: %s",box_table[cf_to_box(cf)].name);
		}

		return(0);
	}

	cardfile_changed[cf] = 0;

	if(multiprocess & 1)
	{
		set_lock_lists(5,fr);
	}

	/* write out cards, but ignore those which have been deleted */

	card_header.version = 1;
	card_header.today_date = today_date;  /* when Pluto started running */
	fwrite(&card_header,1,sizeof(card_header),f_out);

	n_cards = 0;
	for(card_num = 0; card_num < fr->n_entries; card_num++)
	{
		if(multiprocess & 1)
		{
			if(lock_lists != 5)
			{
				cardfile_autosave_abandon();
				break;   /* abandoned */
			}
			if((card_num & 0xff)==0)
				call_event_process(0);
		}

		cp = cardfile_cptr(fr,card_num);
		box = cp->date_box >> 26;

		if(cf==0)
		{
			if((box >= BOX_EXTERN) && (box != BOX_BIN))
				continue;
		}
		else
		{
			if(box != (cf - 1 + BOX_EXTERN))
				continue;
		}

		/* keep BOX_BIN but not BOX_DELETED */
		length = card_size(cp);
		if(fwrite_werr(cp,length,1,f_out) < 0)
			break;
		n_cards++;
	}

	card_header.n_cards = n_cards;

	fflush(f_out);
	rewind(f_out);
	fwrite(&card_header,1,sizeof(card_header),f_out);
	fclose(f_out);


	clear_lock_lists(5);

	return(n_cards);
}   /* end of cardfile_save */




void cardfile_autosave(int time, void *handle)
/********************************************/
{
	int  cf;
	int  count;
	static int  last_cf = 0;

	if((lock_lists == 0) && (cardfile_autosave_timer > 0))
	{
		cardfile_autosave_timer--;

		if(cardfile_autosave_timer == 0)
		{
			/* only save one file. look at external boxes first */
			cf = last_cf+1;
			for(count=0; count <= N_BOXES_EXTERN; count++)
			{
				if(cf > N_BOXES_EXTERN)
					cf = 0;

				if(cardfile_changed[cf] && (cardfile[cf].path[0] != 0))
				{
					last_cf = cf;
					if(cardfile_save(cf,3) > 0)
						break;    /* only save one file */
				}
				cf++;
			}
		}
	}

	alarm_set(alarm_timenow() + 60*100,cardfile_autosave, (void *)1); /* 1 minute */

}   /* end of cardfile_autosave */




/*************************************************************************/
/*  List Editing */
/*************************************************************************/



int  article_level(FOLDREC *fr, int ix)
/*************************************/
/* Get sorting level of article, not counting extra
   thread indentation levele */
{
	int i;

	if((i = (fr->ixlist[ix] >> IXLEV)) <= fr->display_levels)
		return(i);
	return(fr->display_levels);
}   /* end of article_level */




void list_enumerate_start(FOLDREC *fr)
/************************************/
{
	int  ix;
	int  level;
	int  select_level;
	unsigned int  word;

	visdelay2_begin();
	list_escape_flag = 0;
	fr->enumerate = 0;
	fr->enumerate_count = 0;
	fr->enumerate_count2 = 0;
	fr->enumerate_level = 0x7f;

	clear_ixlist(fr);

	for(ix=0; ix<fr->n_entries; ix++)
	{
		word = fr->ixlist[ix];

		level = word >> IXLEV;

		select_level = ((word >> IXSEL) & IXSELB) - 1;

		if(select_level >= 0)
		{
			fr->enumerate_count++;
			fr->ixlist[ix] = word | IXLIST_MARKED;

			/* don't propagate if we're indented within a thread display */
			if(select_level < fr->display_levels)
				fr->enumerate_level = select_level;
		}
		else if(level > fr->enumerate_level)
		{
			fr->enumerate_count++;
			fr->ixlist[ix] = word | IXLIST_MARKED;
		}
		else
			fr->enumerate_level = 0x7f;
	}

}   /* end of list_enumerate_start */



CARD *list_enumerate_next(FOLDREC *fr, unsigned int *ixlist)
/**********************************************************/
/* Get next selected item in the list.  Follows 'list_start_selected' */
{
	int  word;

	if(list_escape_flag)
	{
		list_escape_flag = 0;
		visdelay2_end();
		werr(0,"List operation cancelled");
		return(NULL);
	}

	while(fr->enumerate < fr->n_entries)
	{
		word = ixlist[fr->enumerate++];
		if(word & IXLIST_MARKED)
		{
			fr->enumerate_count2++;
			visdelay2_percent((fr->enumerate_count2*100)/fr->enumerate_count);
			return((CARD *)((word & IXLIST_MASK) << 2));
		}
	}

	if(fr->enumerate_count == 0)
	{
		werr(0,"No items are selected");
	}

	visdelay2_end();
	return(NULL);   /* end of list */
}   /* end of list_enumerate_next */




void list_selection_clear(FOLDREC *fr)
/************************************/
{
	int  ix;
	unsigned int *ixlist;
	int  linenum;
	int  level;
	int  vector[N_FOLD_LEVELS];

	if((ixlist = fr->ixlist) == NULL)
		return;

	fr->n_selected = 0;


	for(ix=0; ix<fr->n_entries; ix++)
	{
		if(*ixlist & IXLIST_SELECTED)
		{
			level = ((*ixlist >> IXSEL) & IXSELB) - 1;
			if(level >= N_FOLD_LEVELS)
				level = N_FOLD_LEVELS-1;
			fold_ref_to_vector(fr,ix,level,vector);
			linenum = fold_vector_to_line(fr,vector,level);

			redraw_list_oneline(fr,linenum);

			*ixlist &= ~IXLIST_SELECTED;   /* clear selection bits, bits 26-28 */
		}
		ixlist++;
	}
}   /* end of list_selection_clear */




void list_select_all_open(FOLDREC *fr)
/************************************/
/* Select all the articles at this level */
{
	int  ix;
	unsigned int  *ixlist;
	int  level;
	int  level2;   /* level at which articles are marked */
	int  lev;
	int  select_bit;
	int  start_of_level = 0;

	list_selection_clear(fr);
	ixlist = fr->ixlist;

	/* find the first selected item */
	level = ixlist[fr->cursor_ref] >> IXLEV;

	if(level < fr->cursor_level)
	{
		start_of_level = 1;
		level = fr->cursor_level;
	}
	level2 = fr->cursor_level;

	if((level < fr->open_levels) && (fr->open_ref[level] == fr->cursor_ref))
	{
		/* we are on the first of the next level down */
		level++;
		level2 = level;
		selects_level = level;
		start_of_level = 1;
	}

	select_bit = (level2+1) << IXSEL;

	list_set_select(fr,fr->cursor_ref,select_bit);

	lev = ixlist[fr->cursor_ref+1] >> IXLEV;
	if((lev > level) && (level > fr->display_levels))
	{
		/* select just a sub-level within a threaded display */
		for(ix=fr->cursor_ref+1; ix<fr->n_entries; ix++)
		{
			lev = ixlist[ix] >> IXLEV;

			if(lev <= level)
				break;
			list_set_select(fr,ix,select_bit);
		}
		redraw_list_lines(fr,0);
		return;
	}

	for(ix=fr->cursor_ref+1; ix<fr->n_entries; ix++)
	{
		lev = ixlist[ix] >> IXLEV;

		if(lev < level)
			break;
		if((lev == level) || (lev > fr->display_levels))
			list_set_select(fr,ix,select_bit);
	}

	if(start_of_level==0)
	{
		for(ix=fr->cursor_ref-1; ix>=0; ix--)
		{
			lev = ixlist[ix] >> IXLEV;

			if((lev <= level) || (lev > fr->display_levels))
				list_set_select(fr,ix,select_bit);
			if(lev < level)
				break;
		}
	}

	redraw_list_lines(fr,0);
}   /* end of list_select_all_open */





static void list_selection_delete(FOLDREC *fr, int box, int copy)
/***************************************************************/
/* Delete marked articles (box == BOX_BIN), or move them
   to the specified box */
{
	int  ix;
	unsigned int  *ixlist;
	int  i;
	int  level;
	int  lev;
	int  n_open;
	int  open_level;
	int  count;
	int  card;
	int  following_ix=0;
	int  warning;
	int  any_not_selected;
	int  all_selected;
	int  vector[N_FOLD_LEVELS];
	char buf[80];
	CARD *cptr;
	int  before;
	int  delete_level;
	int  redraw_all_flag;
	int  redraw_one_flag;
	int  progress;
	int  total;


	static char *q_delete = "Are you sure you want to delete ALL this group?";
	static char *q_delete1 = "Are you sure you want to delete this message?";

	if((box < BOX_BIN) && (box_table[box].name[0]==0))
	{
		werr(0,"Box %d does not exist",box);
		return;
	}

	if(check_lock_lists(0xffff) != 0)
		return;

	ixlist = fr->ixlist;

#ifdef deleted
	if(fr->box == box)
	{
		if(box == BOX_BIN)
		{
			werr(0,"Attempt to delete items which are already in the BIN box");
			return;
		}
	}
#endif

	if(box != BOX_BIN)
	{
		if(box_password_check(box,1) != 1)
			return;
	}

	/* find a card in the lowest opened section, which is not marked
	   for deletion */

	list_escape_flag = 0;

	open_level = fr->open_levels;
	n_open = fr->n_open[open_level];

	card = -1;
	count = 0;
	for(ix=fr->open_ref[open_level-1]; ix<fr->n_entries; ix++)
	{
		level = (ixlist[ix] >> IXLEV);
		if(level > fr->display_levels)
			level = fr->display_levels;

		if(level <= open_level)
		{
			count++;
			if(count > n_open)
				break;

			if((ixlist[ix] & IXLIST_SELECTED) == 0)
			{
				/* here is an entry that will remain undeleted */
				card = (ixlist[ix] & IXLIST_MASK) << 2;
				cptr = (CARD *)(card);
				break;
			}
		}
	}

	warning = 0;
	locked_count = 0;
	any_not_selected = 0;

	memset(boxes_count_copy,0,sizeof(boxes_count_copy));
	memset(boxes_unread_copy,0,sizeof(boxes_unread_copy));

	/* give a warning if deleting ALL articles in the List */
	for(ix=0; ix<fr->n_entries; ix++)
	{
		if((ixlist[ix] >> IXLEV) == 0)
		{
			level = ((ixlist[ix] >> IXSEL) & IXSELB) - 1;
			if(level !=0 )
			{
				/* this top level item is not selected */
				any_not_selected = 1;
				break;
			}
		}
	}
	if((any_not_selected == 0) && (box == BOX_BIN))
	{
		warning = 1;
		if(!query(q_delete,"DELETE"))
		{
			return;
		}
	}

	visdelay2_begin();


	/* count items to be deleted or moved */
	total = 0;
	for(ix=0; ix<fr->n_entries; ix++)
	{
		if((ixlist[ix] & IXLIST_SELECTED) != 0)
		{
			level = ((ixlist[ix] >> IXSEL) & IXSELB) - 1;
			total += list_delete_block(fr,&ix,level,0,box,2,0,1);
		}
	}

	/* now delete or move them */
	level = -2;
	progress = 0;
	for(ix=0; ix<fr->n_entries; ix++)
	{

		if((ixlist[ix] & IXLIST_SELECTED) != 0)
		{
			if(list_escape_flag)
				break;

			delete_level = level = ((ixlist[ix] >> IXSEL) & IXSELB) - 1;

			if((box==BOX_BIN) && (warning == 0))
			{
				/* moving into BIN box */

				all_selected = 0;

				lev = ixlist[ix] >> IXLEV;
				if(lev > fr->display_levels)
					lev = fr->display_levels;

				if(lev < level)
				{
					/* this is the first item of an opened group.  Is the whole group open? */
					all_selected = 1;
					for(i=ix+1; (i < fr->n_entries) && ((ixlist[i] >> IXLEV) >= level); i++)
					{
						lev = ixlist[i] >> IXLEV;
						if(lev > fr->display_levels)
							lev = fr->display_levels;

						if((lev == level) && ((ixlist[i] & IXLIST_SELECTED)==0))
						{
							all_selected = 0;
							break;
						}
					}
				}

				if(all_selected)
					delete_level--;  /* all of this group is selected, same as deleting the higher level */

				if(fr->display->warn > delete_level)
				{
					visdelay2_end();

					warning=1;   /* only give warning once */

					if(delete_level == fr->display_levels)
					{
						if(!query(q_delete1,"DELETE"))
							return;
					}
					else
					{
						count = 1;
						for(i=ix+1; (i < fr->n_entries) && ((ixlist[i] >> IXLEV) > delete_level); i++)
						{
							count++;
						}

						if(count > 2)
						{
							if(!query(q_delete,"DELETE"))
								return;
						}
					}

					visdelay2_begin();
				}
			}

			i = list_delete_block(fr,&ix,level,0,box,copy,&progress,total);
			if(following_ix == 0)
				following_ix = i+1;
			ix--;
		}
	}
	if(box == BOX_BIN)
		article_delete(NULL,NULL,1,0);   /* finished, close file */

	visdelay2_end();

	if((level == -2) && (list_escape_flag == 0))
	{
		/* no items have been deleted */
		werr(0,"No items are selected");
		return;
	}

	if((locked_count > 0) && (list_escape_flag == 0))
	{
		sprintf(buf,"%d locked articles, delete?",locked_count);
		if(query2(buf,"Delete","Leave"))
		{
			visdelay2_begin();

			level = -1;
			progress = 0;
			for(ix=0; ix<fr->n_entries; ix++)
			{
				if(list_escape_flag)
				{
					list_escape_flag = 0;
					break;
				}

				if(ixlist[ix] & IXLIST_SELECTED)
				{
					level = ((ixlist[ix] >> IXSEL) & IXSELB) - 1;

					i = list_delete_block(fr,&ix,level,1,box,copy,&progress,total);
					if(following_ix == 0)
						following_ix = i+1;
					ix--;
				}
			}

			visdelay2_end();
		}
	}
	if(box == BOX_BIN)
		article_delete(NULL,NULL,1,0);   /* finished, close file */

	list_escape_flag = 0;
	/* free of memory used by article_delete_internet_header if any */
	if(text_rec_temp.text_base != NULL)
	{
		flex_free((flex_ptr)&text_rec_temp.text_base);
		text_rec_temp.text_base = NULL;
		text_rec_temp.text_buf_size = 0;
	}

	/* save cardfile for external box now if many articles moved into it */
	if(box_to_cf(box) > 0)
	{
		if(total > 10)
		{
			visdelay2_begin();
			cardfile_save(box_to_cf(box),0);
			visdelay2_end();
		}
	}

	/* redraw the boxes list */
	redraw_all_flag = 0;
	for(box=0; box<=BOX_BIN; box++)
	{
		redraw_one_flag = 0;

		if(boxes_count_copy[box] != 0)
		{
			before = boxes_count[box];
			if((((boxes_count[box] += boxes_count_copy[box]) == 0) || (before == 0)) &&
					(box_table[box].hide_box == 3))
			{
				redraw_all_flag = 1;
			}
			else
			{
				redraw_one_flag = 1;
			}
		}

		if(boxes_unread_copy[box] != 0)
		{
			before = boxes_unread_count[box];
			if((((boxes_unread_count[box] += boxes_unread_copy[box]) == 0) || (before == 0)) &&
					(box_table[box].hide_box == 2))
			{
				redraw_all_flag = 1;
			}
			else
			{
				redraw_one_flag = 1;
			}
		}
		if(redraw_one_flag)
		{
			boxlist_recalc();  /* we could consider special case of one article moved */

			redraw_boxes_list(box);
		}
	}
	if(redraw_all_flag)
	{
		set_boxlist_extent(7);
	}

	/* find the new reference of this card addresses */


	ix=0;
	ixlist = fr->ixlist;

	while(ix < fr->n_entries)
	{
		if(((ixlist[ix] & IXLIST_MASK) << 2)== card)
		{
			break;
		}
		ix++;
	}
	if(ix == fr->n_entries)
	{
		/* not found, must have been deleted */
		open_level--;
	}

	open_level--;
	if(open_level < 0)
	{
		list_close_all(fr,1);
	}
	else
	{
		if(ix == fr->n_entries)
		{
			fold_open_at_vector(fr,fr->open_vect,open_level);
		}
		else
		{
			fold_ref_to_vector(fr,ix,open_level,vector);
			fold_open_at_vector(fr,vector,open_level);
		}
		set_list_extent(fr,1, 0);
	}
	list_set_title(fr);

	/* redraw any lists which have been affacted */
	fr->redraw_flag = 1;
	for(i=1; i<N_LISTS; i++)
	{
		if((list_fr[i].redraw_flag) && (list_fr[i].window != NULL))
		{
			redraw_list_lines(&list_fr[i],0);
		}
	}

	if((text_view->cptr != NULL) && ((text_view->cptr->date_box >> 26) == BOX_BIN))
	{
		/* the article being displayed has now been deleted */
		text_window_close(text_view);
		close_article_window(text_view);
	}

	fr->n_selected = 0;
	list_fade_icons(fr,wimp_INOSELECT);

	if(fr->n_entries == 0)
	{
		list_delete_window(fr);
	}
}   /* end of list_selection_delete */




void list_fetch_full(FOLDREC *fr, int cancel)
/*******************************************/
{
	CARD *cptr;

	if(check_lock_lists(0xffff) != 0)
		return;

	set_lock_lists(3,fr);
	list_enumerate_start(fr);

	while((cptr = list_enumerate_next(fr,fr->ixlist)) != NULL)
	{
		if(fetch_full(fr,cptr,cancel) < 0)
		{
			visdelay2_end();
			break;
		}
		call_event_process(0);
	}
	clear_lock_lists(3);
	redraw_list_lines(fr,0);
}   /* end of list_fetch_full */





void list_add_addresses(FOLDREC *fr, int dist_list)
/*************************************************/
/* dist_list 0: to to address book
             1: make distribution list */
{
	int  ix;
	CARD *cptr;
	char *author;
	char *p;
	char *p1;
	FILE *f = NULL;
	char fname[128];

#define N_DIST_ADDR 500
	int n_dist_addr=0;
	char *addresses[N_DIST_ADDR];

	if(check_lock_lists(0xffff) != 0)
		return;

	set_lock_lists(3,fr);
	list_enumerate_start(fr);

	if(dist_list==0)
		addrlist_add_email(NULL,1);   /* start */


	while((cptr = list_enumerate_next(fr,fr->ixlist)) != NULL)
	{
		author = &cptr->data[cptr->d_author];
		if(dist_list==1)
		{
			p = reply_extract_email_addr(author,2);
			for(ix=0; ix<n_dist_addr; ix++)
			{
				if(strcmp_lc(p,addresses[ix])==0)
					break;
			}
			if((ix == n_dist_addr) && (n_dist_addr < N_DIST_ADDR-1))
			{
				p1 = malloc(strlen(p)+1);
				strcpy(p1,p);
				addresses[n_dist_addr++] = p1;
			}
		}
		else
		{
			addrlist_add_email(author,2);
		}
//      call_event_process(0);
	}

	if(dist_list==1)
	{
		sprintf(fname,"%s.MailLists.tmp_list",path_choices);
		f = fopen_werr(fname,"w",NULL);
		if(f != NULL)
		{
			for(ix=0; ix<n_dist_addr; ix++)
			{
				fprintf(f,"%s\n",addresses[ix]);
				free(addresses[ix]);
			}
			fclose(f);
		}
		dataopen(fname,0xfff);
		sprintf(fname,"%s.MailLists",path_choices);
		dirlist_open(fname,"-si");
	}
	else
	{
		addrlist_add_email(NULL,3);
	}

	clear_lock_lists(3);
}   /* end of list_add_addresses */






void card_changed_status(CARD *cptr)
/******************************/
/* Don't change the state of this card from unread to read */
{
	if((cptr != NULL) && (cptr == card_been_read))
		card_been_read = NULL;
}   /* end of card_changed_status */




/*************************************************************************/
/*************************************************************************/



int card_size(CARD *cptr)
/*********************/
/* Find size of card data, as written into the article file */
{
	int  length;
	int  d_end;
	static int d_end_tab[] = {0,0x120,0x140,0x180,0x1c0};

	if(cptr == NULL)
		return(0);

	d_end = cptr->d_end;
	if(d_end <= 3)
	{
		d_end = d_end_tab[d_end];
	}
	length = cptr->data - (char *)cptr + d_end;
	length = (length + 3) & ~0x3;   /* align to word boundary */
	return(length);
}   /* end of card_size */





int pack_card(CARD *cp, CARD_EXPANDED *cardex)
/***************************************/
/* Pack a card into the card file at 'cp'.  Return the length (including padding
   to word boundary) */
{
	int  i;
	short *p_short;
	unsigned int *p_uint;
	char *p;
	char *p_start;
	int  len_keywords;
	int  len_title;
	int  len_author;
	int  len_comment;
	int  length;
	int  j;
	int  min;
	int  min_pos;
	int  d_end;
	int  lengths[4];


	cp->addr = cardex->addr;
	// cardex->reply is STATUS_BIT_RE, set if Re: found in subject, or the message has References or In-reply-to
	cp->status = (cardex->status & STATUS_MASK)
				 | (cardex->replied << 3) | (cardex->reply << 7) | cardex->status_other;
	cp->date_box = (cardex->date & DATE_MASK) | (cardex->docbox << 26);
	cp->user = cardex->flags | (cardex->user & 0x1f);

	cp->msgid = cardex->msgid;
	cp->parent = cardex->parent;
	cp->score = cardex->score;

	len_keywords = strlen(cardex->keywords);
	len_title = strlen(cardex->title);
	len_author = strlen(cardex->author);
	len_comment = strlen(cardex->comment);


	if(cardex->n_cats >= CD_N_CATS)
		cardex->n_cats = (CD_N_CATS-1);

	length = len_keywords + len_title + len_author + len_comment +
			 cardex->n_refs*4 + cardex->n_cats*2 + 7;
	if(length > 0x180)
	{
		i = length - 0x180;   /* reduce by this much */
		if(len_title > (i + 40))
		{
			cardex->title[len_title-i] = 0;
			len_title = strlen(cardex->title);
		}
		else if(len_comment > (i + 40))
		{
			cardex->comment[len_comment-i] = 0;
			len_comment = strlen(cardex->comment);
		}
		else if(len_author > (i + 40))
		{
			cardex->author[len_author-i] = 0;
			len_author = strlen(cardex->author);
		}
		else
		{
			/* variable length data is too long */
			return(-1);
		}
	}


	cp->source = cardex->source;

	p_uint = (unsigned int*)cp->data;
	p_start = cp->data;

	cp->n_refs = cardex->n_refs;
	for(i=0; i<cardex->n_refs; i++)
	{
		*p_uint++ = cardex->references[i];
	}

	p_short = (short *)p_uint;
	cp->n_cats = cardex->n_cats;
	for(i=0; i<cardex->n_cats; i++)
	{
		*p_short++ = cardex->cat_codes[i];
	}
	cp->n_cats |= cardex->status_other2;



	p = (char *)p_short;

	/* put the longest string at the end */
	lengths[0] = len_keywords;
	lengths[1] = len_title;
	lengths[2] = len_author;
	lengths[3] = len_comment;

	for(i=0; i<4; i++)
	{
		min = 0x7fff;
		min_pos = 0;

		for(j=0; j<4; j++)
		{
			if(lengths[j] < min)
			{
				min = lengths[j];
				min_pos = j;
			}
		}

		switch(min_pos)
		{
		case 0:
			cp->d_keywords = p - p_start;
			strcpy(p,cardex->keywords);
			break;

		case 1:
			cp->d_title = p - p_start;
			strcpy(p,cardex->title);
			break;

		case 2:
			cp->d_author = p - p_start;
			strcpy(p,cardex->author);
			break;

		case 3:
			cp->d_comment = p - p_start;
			strcpy(p,cardex->comment);
			break;
		}
		p += (lengths[min_pos] + 1);
		lengths[min_pos] = 0x7fff;  /* don't select this one again */
	}


	cp->d_end = d_end = p - p_start;
	if(d_end > 255)
	{
		if(d_end > 0x140)
		{
			cp->d_end = 3;
		}
		else if(d_end > 0x120)
		{
			cp->d_end = 2;
		}
		else
		{
			cp->d_end = 1;
		}
	}

	length = card_size(cp);

	cp->alength = (length + cardex->text_length) | (cardex->colour << 24) | (cardex->ftype << 27);
	return(length);
}   /* end of pack_card */




void unpack_card(CARD *cp, CARD_EXPANDED *cardex)
/***********************************************/
{
	int  i;
	short *p_short;
	unsigned int *p_uint;

	cardex->card_ptr = cp;

	cardex->addr = cp->addr;
	cardex->flags = cp->user & 0xe0;
	cardex->user = cp->user & 0x1f;
	cardex->docbox = cp->date_box >> 26;
	cardex->copy_box = 0;

	cardex->msgid = cp->msgid;
	cardex->parent = cp->parent;
	cardex->score = cp->score;

	cardex->status = cp->status & STATUS_MASK;
	cardex->status2 = cp->status & STATUS_MASK2;
	cardex->replied = (cp->status >> 3) & 1;
	cardex->reply = (cp->status >> 7) & 1;
	cardex->status_other = cp->status & 0x70;   /* bits 4,5,6 */
	cardex->status_other2 = cp->n_cats & 0xe0;  /* bits 5,6,7 */
	cardex->colour = (cp->alength >> 24) & 7;
	cardex->ftype = (cp->alength >> 27);

	cardex->text_offset = card_size(cp);
	cardex->text_length = (cp->alength & ART_LENGTH_MASK) - cardex->text_offset;
	cardex->text_changed = 0;
	cardex->text_anchor = NULL;
	cardex->text_start = 0;
	cardex->text_start_body = 0;


	p_uint = (unsigned int *)cp->data;

	cardex->date = cp->date_box & DATE_MASK;
	cardex->source = cp->source;

	cardex->n_refs = cp->n_refs & N_REFS_MASK;
	for(i=0; i<cardex->n_refs; i++)
	{
		cardex->references[i] = *p_uint++;
	}

	p_short = (short *)p_uint;
	cardex->n_cats = cp->n_cats & N_CATS_MASK;
	for(i=0; i<cardex->n_cats; i++)
	{
		cardex->cat_codes[i] = *p_short++;
	}
	/* ignore last category if it is zero */
	if((i > 0) && (cardex->cat_codes[i-1] == 0))
		cardex->n_cats--;

	strncpy0(cardex->keywords,&cp->data[cp->d_keywords],sizeof(cardex->keywords));
	strncpy0(cardex->title,&cp->data[cp->d_title],sizeof(cardex->title));
	strncpy0(cardex->author,&cp->data[cp->d_author],sizeof(cardex->author));
	strcpy(cardex->comment,&cp->data[cp->d_comment]);
	strcpy(cardex->cats,expand_cats(cardex));

}   /* end of unpack_card */





CARD *cardfile_add(CARD_EXPANDED *cardex)
/***************************************/
/* Add a card to the card file */
/* returns cptr = OK
   NULL         memory allocation failed
   1            error for this record only */
{
	int  length;
	CARD *cptr;
	CARD card;
	char *p;
	FOLDREC *fr;
	int  text_start;
	int  text_length;
	int  save_flags;
	int  size;

	text_start = cardex->text_start;
	text_length = cardex->text_length;
	save_flags = cardex->flags;

	/* check for stripping internet header */
	if(box_table[cardex->docbox].flags & BOX_STRIP_HEADERS)
	{
		if(cardex->text_start_body > cardex->text_start)
		{
			text_length -= (cardex->text_start_body - cardex->text_start);
			text_start = cardex->text_start_body;
		}
	}
	else
	{
		if(cardex->text_start_body > cardex->text_start)
		{
			cardex->flags |= INET_HDR_MASK;
		}
	}


	length = pack_card(&card,cardex);
	if(length < 0)
	{
		werr(0,"Error: Header strings are too long");
		return((CARD *)1);
	}
	cptr = malloc(length);
	if(cptr==NULL)
	{
		malloc_err(7);
		return(NULL);
	}
	if(((int)cptr & ~(IXLIST_MASK<<2)) != 0)
	{
		/* address allocated is to large to fit into IXLIST */

		/* try re-allocating ixlist and hope this frees some lower addressed memory */
		free(cptr);
		size = fr->n_entries_max * sizeof(int);
		p = malloc(size);
		if(p != NULL)
		{
			memcpy(p,fr->ixlist,size);
			free(fr->ixlist);
			fr->ixlist = (unsigned int *)p;
		}

		cptr = malloc(length);
		if((cptr == NULL) || (((int)cptr & ~(IXLIST_MASK<<2)) != 0))
		{
			werr(0,"Address limit reached when allocating a new index record.  Please delete unwanted articles, then quit and reload Pluto");
			return(NULL);
		}
	}
	memcpy(cptr,&card,length);
	cardex->card_ptr = cptr;
//   cardfile[0].n_entries++;
	fr = &list_fr[0];

	if((fr->n_entries+1) >= fr->n_entries_max)
	{
		/* ixlist is full, extend it */
		p = realloc(fr->ixlist,(fr->n_entries_max+CARD_INDEX_EXTRA)*sizeof(int));
		if(p==NULL)
		{
			malloc_err(8);
			return(NULL);
		}
		fr->ixlist = (unsigned int *)p;
		fr->n_entries_max += CARD_INDEX_EXTRA;
	}
	fr->ixlist[fr->n_entries++] = ((unsigned int)cptr) >> 2;
	if(fr->n_entries_tot < fr->n_entries)
		fr->n_entries_tot = fr->n_entries;

	cardfile_autosave_set(cptr_to_cf(cptr));

	if(article_add(cptr,cardex->text_anchor,text_start,text_length)!=0)
		cptr=NULL;   /* failed to update article files - this is very bad */

	cardex->flags = save_flags;

	/* top section of the main index is no longer sorted */
	main_index_sorted2 = 0;

	return(cptr);
}   /* end of cardfile_add */




void cardfile_remove3(FOLDREC *fr, CARD *cptr)
/********************************************/
/* Simple move article to BIN, don't update article lists */
{
	int box1;
	int  x1,x2;

	card_index_change(cptr,NULL,NULL);   /* remove from all lists */
	box1 = cptr->date_box >> 26;
	article_delete(fr,cptr,1,6);        /* and box -> BIN */

	x1 = boxlist_adjust_count(box1,-1,cptr->status);
	x2 = boxlist_adjust_count(BOX_BIN,1,cptr->status);
	if(x2 > x1) x2 = x1;

	if(x2 == 2)
		set_boxlist_extent(7);  /* boxes may become hidden/unhidden */
	else if(x1 == 1)
		boxlist_recalc();       /* boxes status may have changed */

	redraw_list_lines(NULL,0);
	cardfile_autosave_set(cptr_to_cf(cptr));
}   /* end of cardfile_remove3 */




void cardfile_remove2(FOLDREC *fr, CARD *cptr, int box)
/*****************************************************/
/* Remove a card from the card file and its article from the article file */
{
	int  box1;   /* curent box */
	int  x1, x2;

	if(cptr == NULL)
		return;

	if(check_lock_lists(0xffff) != 0)
		return;

	box1 = cptr->date_box >> 26;

	if(box != BOX_BIN)
	{
		card_index_change(cptr,NULL,NULL);   /* remove from only it's current list ? */

		if(box1 != box)
		{
			if(box1 == BOX_BIN)
			{
				article_delete(fr,cptr,1,1);   /* resurrect article from bin */
			}
			cptr->date_box = (cptr->date_box & ~BOX_MASK) + (box << 26);

			if(box_table[box].flags & BOX_STRIP_HEADERS)
				article_delete_internet_header(fr,cptr,0);
			else
				article_update(fr,cptr,NULL,0,0);
		}
	}
	else
	{
		card_index_change(cptr,NULL,NULL);   /* remove from all lists */

		if(box1 == BOX_BIN)
			cptr->status = (cptr->status & ~STATUS_MASK2) | STATUS_HIDDEN;  /* already in bin */

		article_delete(fr,cptr,1,4);     /* and box -? BIN */
	}

	x1 = boxlist_adjust_count(box1,-1,cptr->status);
	x2 = boxlist_adjust_count(box,1,cptr->status);
	if(x2 > x1) x2 = x1;

	if(x2 == 2)
		set_boxlist_extent(7);  /* boxes may become hidden/unhidden */
	else if(x1 == 1)
		boxlist_recalc();       /* boxes status may have changed */


	/* re-display all the article lists */

	if((fr != NULL) && (fr != &list_fr[0]))
	{
		while((fr->open_levels > 0) && (fr->n_open[fr->open_levels]==0))
		{
			/* we have removed the last article from this level, close it */
			fr->open_levels--;
			fr->n_open[fr->open_levels]--;
		}
		set_list_extent(fr,1, 0);

		if(fr->n_entries == 0)
			list_delete_window(fr);
	}
	redraw_list_lines(NULL,0);   /* needn't redraw the whole window if we knew the line for this card */

	cardfile_autosave_set(box_to_cf(box1));
	cardfile_autosave_set(box_to_cf(box));
}   /* end of cardfile_remove2 */




int cardfile_remove(TEXTR *t, CARD *cptr, int box)
/************************************************/
{
	FOLDREC *fr = NULL;

	if(check_lock_lists(0xffff) != 0)
		return(-1);

	if(t != NULL)
	{
		fr = t->fr;
		t->just_removed = 1;
	}

	if(cptr == card_been_read)
	{
		card_set_read(fr);
	}
	cardfile_remove2(fr,cptr,box);
	return(0);
}   /* end of cardfile_remove */






static CARD *cardfile_reallocate(CARD *cptr, int length)
/******************************************************/
/* Free 'cptr' and allocate a new entry */
{
	int  cf;
	int  found;
	char *p;
	CARD *new_cptr;

	new_cptr = malloc(length+4);    /* the +4 is just for luck :-) */

	if((new_cptr == NULL) || (((int)new_cptr & ~(IXLIST_MASK<<2)) != 0))
	{
		malloc_err(9);
		return(NULL);
	}
	cardfile[0].n_entries++;

	main_index_sorted2 = 0;
	card_index_change(cptr,new_cptr,NULL);

	/* only free the cptr if card was allocated separately, and is not in a cardfile */
	found = 0;
	for(cf=0; cf<(N_BOXES_EXTERN+1); cf++)
	{
		if((p = cardfile[cf].base) != NULL)
		{
			if(((char *)cptr >= p) && ((char *)cptr < &p[cardfile[cf].length]))
			{
				found = 1;
				break;
			}
		}
	}

	if(!found)
		free(cptr);
	else
		cptr->date_box = (cptr->date_box & ~BOX_MASK) | ((unsigned)BOX_DELETED << 26);

	return(new_cptr);
}   /* end of cardfile_reallocate */







void cardfile_update(FOLDREC *fr, TEXTR *t, CARD_EXPANDED *cardex, int redraw)
/*******************************************************************/
{
	int  length;
	int  old_length=0;
	int  addr;
	CARD *cptr;
	CARD *old_cptr;
	CARD card;

	if(check_lock_lists(0x10008) != 0)   /* allow reason 3 */
		return;

	length = pack_card(&card,cardex);
	if(length < 0)
	{
		werr(0,"Header strings are too long");
		return;
	}

	cptr = cardex->card_ptr;
	if(cptr != NULL)
	{
		old_length = card_size(cptr);

		addr = cptr->addr;
	}

	if(length != old_length)
	{
		if(cptr == NULL)
			return;

		if(length > old_length)
		{
			/* allocate a new cardfile entry */
			old_cptr = cptr;
			cptr = cardfile_reallocate(old_cptr,length);
			cardex->card_ptr = cptr;

			if(card_been_read == old_cptr)
				card_been_read = cptr;

			if((t != NULL) && (t->cptr == old_cptr))
				t->cptr = cptr;
		}
	}

	memcpy(cptr,&card,length);

	if((t!=NULL) && (t->changed))
		article_update(t->fr,cptr,&t->text_base,0,t->text_length);
	else
		article_update(fr,cptr,NULL,0,0);  /* ???? t not set */

	unpack_card(cptr,cardex);  /* in case the address has changed */

	/* re-display the article list */

	/* search all open list windows for this cptr.
	   fold_ref_to_line(fr,ref,level)  to find the line number (if open) in list.
	   redraw just this this line ?
	   No, redraw lines for each level up (in case status icons have changed)

	   Need to redraw list from this line down if we delete an item
	*/

	if(redraw)
	{
		redraw_list_lines(NULL,0);
	}

	cardfile_autosave_set(cptr_to_cf(cptr));
}   /* end of cardfile_update */




#ifndef ARGO

int card_add_category(TEXTR *textr_cats, int cat)
/***********************************************/
/* Called from category module when click on category */
{

	int  i;
	CARD_EXPANDED *cardex;

	if(textr_cats == NULL)
		return(0);

	if(cat == 0)
		return(0);

	cardex = textr_cats->cardex;

	if(cardex->n_cats >= (CD_N_CATS-1))
		return(0);

	/* look though category list to see whether it is already present */
	for(i=0; i<cardex->n_cats; i++)
	{
		if(cardex->cat_codes[i] == cat)
			break;
	}

	if(i < cardex->n_cats)
	{
		/* already present */
		return(1);
	}

	/* add to list */
	cardex->cat_codes[cardex->n_cats++] = cat;
	cardex->header_changed = 1;
	cardfile_update(textr_cats->fr,NULL,cardex,0);

	/* re-display the categories field */
	strcpy(cardex->cats,expand_cats(cardex));
	dbox_setfield(textr_cats->dbox_card,CD_CATS,cardex->cats);
	open_article(textr_cats,0);

	return(1);
}   /* end of card_add_category */





int card_identify_cat(TEXTR *t, int index, int action)
/****************************************************/
/* Find category number from index into card's category string */
{
	int i;
	int catix;
	int catnum;
	char *p;
	CARD_EXPANDED *cardex;

	cardex = t->cardex;

	catix = 0;
	p = cardex->cats;
	for(i=0; i<index; i++)
	{
		if(p[i] <= ' ')
			catix++;
	}
	catnum = cardex->cat_codes[catix];

	if(action == 1)
	{
		/* move this category to the front of the list */
		for(i=catix; i>0; i--)
		{
			cardex->cat_codes[i] = cardex->cat_codes[i-1];
		}
		cardex->cat_codes[0] = catnum;

		/* remake ascii category list */
		strcpy(cardex->cats,expand_cats(cardex));
		dbox_setfield(t->dbox_card,CD_CATS,cardex->cats);
	}


	return(catix);
}   /* end of card_identify_cat */


#endif




int card_add_source(int action, int source)
/*****************************************/
/* Called from category module when click on source name */
{
	/* no longer needed now that we have Sources menu */
	return(1);
}   /* end of card_add_source */




void set_card_height(TEXTR *t, CARD_EXPANDED *cardex,int height)
/**************************************************************/
{
	static char bar_heights[2][4] = {
		122,144,166,0,
		122,150,174,0,
	};

	if(height == -1)
	{
		t->button_bar_expanded = 0;

		/* initial opening of article */
		if(options.article_viewer_type == 0)
		{
			if(cardex->comment[0] != 0)
			{
				t->button_bar_expanded = 1;
			}

			if(cardex->keywords[0] != 0)
			{
				t->button_bar_expanded = 2;
			}
		}
		else
		{
			if(cardex->cats[0] != 0)
			{
				t->button_bar_expanded = 1;
			}
			if(cardex->keywords[0] != 0)
			{
				t->button_bar_expanded = 2;
			}
		}
	}
	else
	{
		t->button_bar_expanded = height;
	}

	t->button_bar_height = bar_heights[options.article_viewer_type&1][t->button_bar_expanded]*2;
}   /* end of set_card_height */






void crosspost_remove(FOLDREC *fr, CARD *cptr1, int action)
/*********************************************************/
/*  Action=1  mark as read,
           2  delete
*/
{
	int  ix;
	CARD *cptr;
	int  n_entries;
	int  msgid;
	int  date;
	int  length;
	unsigned int *ixlist;
	int  open_level;
	int  vector[N_FOLD_LEVELS];

	if(action==0)
		return;

	if(check_lock_lists(0x10008) != 0)   /* allow reason 3 */
		return;

	date = cptr1->date_box & DATE_MASK;
	length = cptr1->alength & ART_LENGTH_MASK;
	msgid = cptr1->msgid;
	cptr1->user &= (~STATUS_BIT_CROSSPOST);

	n_entries = list_fr[0].n_entries;
	ixlist = list_fr[0].ixlist;

	for(ix=0; ix<n_entries; ix++)
	{
		cptr = (CARD *)((ixlist[ix] & IXLIST_MASK) << 2);
		if((cptr->msgid == msgid) && (cptr != cptr1) && ((cptr->date_box & DATE_MASK)==date) && ((cptr->alength & ART_LENGTH_MASK)==length))
		{
			if(cptr->user & STATUS_BIT_CROSSPOST)
			{
				cptr->user &= (~STATUS_BIT_CROSSPOST);
				if(action == 1)
				{
					cptr->status = (cptr->status & ~STATUS_MASK) + STATUS_READ;
					article_update(fr,cptr,NULL,0,0);
				}
				else if(action == 2)
				{
					cptr->status = (cptr->status & ~STATUS_MASK) + STATUS_READ;
					cardfile_remove2(fr,cptr,BOX_BIN);
				}
			}
		}
	}
	if((action == 2) && (fr != NULL))
	{
		open_level = fr->open_levels-1;
		fold_ref_to_vector(fr,fr->cursor_ref,open_level,vector);
		fold_open_at_vector(fr,vector,open_level);
	}

}   /* end of crosspost_remove */



void card_remove_fetching_status(CARD_EXPANDED *cardex)
/*****************************************************/
{
	if(cardex->status == (STATUS_FETCHING & STATUS_MASK))
		cardex->replied = 0;  /* remove replied bit from STATUS_FETCHING */
}



void card_set_read(FOLDREC *fr)
/*****************************/
/* Is there an article which is waiting to be marked as read ? */
{
	int  linenum;
	int  box;
	int  i;

	if(card_been_read != NULL)
	{
		if((card_been_read->status & STATUS_MASK2) == STATUS_FETCHING)
		{
			/* don't change to "read" if we've requested full body */
			return;
		}

		box = card_been_read->date_box >> 26;

		if((box_table[box].flags & BOX_LEAVE_UNREAD) != 0)
		{
			/* don't change from Unread to Read automatically for this Box */
			return;
		}

		if((card_been_read->status & STATUS_MASK) <= STATUS_UNREAD)
		{
			cardfile_autosave_set(box_to_cf(box));

			card_been_read->status = (card_been_read->status & ~STATUS_MASK) + STATUS_READ;

			/* decrement unread count */
			if((i =boxes_unread_inc(box,-1)) == 1)
				boxlist_recalc();
			else if(i == 2)
				set_boxlist_extent(7);   /* box read/unread status has changed */
			redraw_boxes_list(box);

			linenum = fold_linenum_open(fr,fr->open_levels);

			linenum = fold_ref_to_line(fr,card_been_read_ref,fr->open_levels);
			if(linenum >= 0)
			{
				redraw_list_oneline(fr,linenum);
			}
			linenum = fold_linenum_open(fr,fr->open_levels-1);
			if(linenum >= 0)
			{
				redraw_list_oneline(fr,linenum);
			}
		}

		if(card_been_read->user & STATUS_BIT_CROSSPOST)
		{
			crosspost_remove(fr,card_been_read,options.crossposts);
		}
		card_been_read = NULL;
	}
}  /* end of card_set_read */









int text_count_newsgroups(TEXTR *t)
/*********************************/
/* Count number of newsgroups in the internet header */
{
	int  i;
	char *p;
	int  count=0;

	p = t->text_base;
	for(i=0; i<t->internet_header; i++)
	{
		if((i==0) || (p[i-1] == '\n'))
		{
			if(memcmp(&p[i],"Newsgroups: ",12) == 0)
			{
				count=0;
				p = &p[i+12];
				while((p[0] != ':') && ((p[0]!='\n') || (p[-1]!='\n')))
				{
					if((*p == ',') || (*p=='\n'))
						count++;
					p++;
				}
			}
		}
	}
	return(count);
}   /* end of text_count_newsgroups */



int card_check_changed(TEXTR *t)
/******************************/
{
	static char *string=
		"Article contains unsaved changes";

	if(t == NULL)
		return(0);

	if((t->text_type <= X_VIEW2) && (t->changed & 1))
	{
		if(os_version >= 0xa5)
		{
			switch(query_dcs(string))
			{
			case 3:   /* Save */
				text_save_article(t);
				return(0);
			case 4:   /* Cancel */
				return(1);
			case 5:   /* Discard */
				break;
			}
		}
		else
		{
			if(query(string,"Discard") == 0)
				return(1);
		}
		t->changed = 0;
	}
	return(0);
}   /* end of card_check_changed */




int card_mail_type(CARD_EXPANDED *cardex, char *source_name)
/**********************************************************/
/* 1=news, 2=private, 3=copied, 4=maillist */
{
	if(cardex->status_other & STATUS_BIT_NEWS)
		return(1);

	if(cardex->status_other2 & STATUS_BIT_MAILLIST)
	{
		/* look for COPIED in maillist messages ? */
		if(cardex->status_other2 & STATUS_BIT_CC)
			return(4);
		else
			return(3);   /* CC bit is inverted for maillist messages */
	}

	if(cardex->status_other & STATUS_BIT_OG)
	{
		if((cardex->status_other2 & STATUS_BIT_CC) == 0)
			return(3);   /* CC bit is inverted for OG messages */
	}
	else
	{
		if(cardex->status_other2 & STATUS_BIT_CC)
			return(3);
	}



	if(memcmp(&source_name[1],"ail.",4)==0)
		return(2);

	return(0);
}   /* end of card_mail_type */




int any_top_bit_set(char *string)
/*******************************/
/* Look for any top-bit-set characters */
{
	int  c;

	while((c = *string++) != 0)
	{
		if(c >= 0xa0)
			return(1);
	}
	return(0);
}   /* end of any_top_bit_set */




int card_edit(TEXTR *t, FOLDREC *fr, CARD *cptr, int action, int ref)
/*******************************************************************/
/* Action bit 0  0=update, 1=edit
              1  1=mark previus article as read
              8  1=allow edit */
{
	dbox  d;
	int  i;
	int  mail_type;
	int  news_mail;
	CARD_EXPANDED *cardex;
	int  font;
	char *date_str;
	int  date_now;
	static int author_field = -1;
	char buf[128];
	static char *author_field_name[] = {"Author","To"};


	static char *mail_types[] = {"","NEWS","PRIVATE","COPIED","MAILLIST"};
	static char colours[] = {1,1,1,12,15};

	if(t == NULL)
		t = text_view;

	if(cptr == NULL)
	{
		werr(0,"Error: Null pointer in card_edit");
		return(2);
	}

	if(check_lock_lists(0x10014) != 0)
		return(1);    /* allow during search or debatch */

	if(t == text_view)
	{
		if(text_view_iconised)
		{
			/* the previous article in the article viewer has been iconised.
			   convert it into an "additional article viewer" so we can re-use
			   the article viewer window */

			text_view = text_data_init(TEXT_EXTRA,X_VIEW);
			if(text_view == NULL)
			{
				text_view = t;
			}
			else
			{
				t->text_type = X_VIEW2;
				text_fade_buttons(t);

				t = text_view;
			}

			text_view_iconised = NULL;
		}
	}


	if(card_check_changed(t)!=0)
		return(1);

	visdelay2_begin();

	cardex = t->cardex;

	if(action & 2)
	{
		/* mark previous article as read */
		card_set_read(fr);
	}

	if(ref >= 0)
	{
		card_been_read = cptr;
		card_been_read_ref = ref;
	}
	else
	{
		card_been_read = NULL;
	}

	unpack_card(cptr ,cardex);
	if(cardex->status_other & STATUS_BIT_NEWS)
		news_mail = 2;
	else
		news_mail = 1;

	if(cardex->status <= STATUS_NEW)
	{
		/* reading a newly fetched article */
		box_any_read[cardex->docbox] |= news_mail;
		box_any_read[cardex->source + N_BOXES] |= news_mail;
	}

	t->fr = fr;
	t->parent = fr;
	t->cardex = cardex;

	d = t->dbox_card;


	dbox_setfield(d,CD_TITLE,cardex->title);
	dbox_setfield(d,CD_AUTHOR,cardex->author);
	dbox_setfield(d,CD_COMMENT,cardex->comment);
	dbox_setfield(d,CD_KEYS,cardex->keywords);
	dbox_setfield(d,CD_CATS,cardex->cats);
	dbox_setfield(d,CD_BOXIN,get_box_name(cardex->docbox));

	if(cardex->docbox == BOX_BIN)
		i = 15;   /* blue */
	else
		i = 1;    /* grey */
	wimp_set_icon_state((wimp_w)dbox_syshandle(d),CD_BOXIN,i << 28,0xf0000000);

	/* set Author field to Author or To */
	i=0;
	if(cardex->status_other & STATUS_BIT_OG)
		i = 1;

	dbox_setfield(d,CD_AUTHOR_LABEL,author_field_name[i]);
	author_field = i;

	if(fr != NULL)
	{
		if(fr->display_levels == 0)
		{
			t->level_index = ref+1;
			t->level_total = fr->n_entries;
		}
		else
		{
			i = fold_count_at_level(fr,ref,fr->display_levels,&t->level_total);
			t->level_index = ref-i+1;
			t->level_total -= i;
		}
	}

	set_card_height(t,cardex,-1);

	t->minutes = 0;
	if(text_read_article(t,cardex, cardex->addr, cardex->text_offset, cardex->text_length,2,0) < 0)
	{
		visdelay2_end();
		return(4);
	}


	date_str = decode_date(cardex->date,0xc);
	if(t->minutes > 0)
	{
		sprintf(&date_str[17],"%.2d",t->minutes-1);   /* minutes from Date: in internet header */
	}

	/* check whether the article is later than now */
	date_now = get_today_date2(buf);
	if(cardex->date > date_now)
		i = 15;   /* blue */
	else
		i = 1;    /* grey */
	wimp_set_icon_state((wimp_w)dbox_syshandle(d),CD_DATE,i << 28,0xf0000000);

	dbox_setfield(d,CD_DATE,date_str);

	if(action & 0x100)
	{
		t->allow_edit = 1;
		text_autosave(0,t);
	}
	/* count number of newsgroups */
	if(t->followup_set)
		i = 15;
	else
		i = 1;   /* grey */
	wimp_set_icon_state((wimp_w)dbox_syshandle(d),CD_SOURCE,i << 28,0xf0000000);

	if(t->n_newsgroups > 1)
		sprintf(buf,"%s + %d",expand_source(cardex->source,1),t->n_newsgroups-1);
	else
		sprintf(buf,"%s",expand_source(cardex->source,1));
	dbox_setfield(d,CD_SOURCE,buf);

	mail_type = card_mail_type(cardex,buf);
	dbox_setfield(d,CD_MAILTYPE,mail_types[mail_type]);

	wimp_set_icon_state((wimp_w)dbox_syshandle(d),CD_MAILTYPE,colours[mail_type] << 28,0xf0000000);

	sprintf(buf,"Size: %d",t->text_length);
	dbox_setfield(d,CD_LENGTH,buf);

	text_show_status(t);


	/* use Latin# variants ? */
	/* This is of no use, unless we set cardex->charset_title from information stored
	   in the CARD record.  Or if we assume it's the same as the Text charset */
	if(any_top_bit_set(cardex->title))
		cardex->charset_title = t->charset;
	if(any_top_bit_set(cardex->author))
		cardex->charset_author = t->charset;

	if(os_version > 0xa6)
	{
		/* bug in 3.5 and 3.6, desktop font reverts to system font */
		i = 0x17000000;
		font = font_find_variant(desktop_font_name,cardex->charset_title,192,192);
		if(font > 0)
		{
			i = (font << 24) | 0x40;
		}
		wimp_set_icon_state((wimp_w)dbox_syshandle(d),CD_TITLE,i,0xff000040);
		i = 0x17000000;
		font = font_find_variant(desktop_font_name,cardex->charset_author,192,192);
		if(font > 0)
		{
			i = (font << 24) | 0x40;
		}
		wimp_set_icon_state((wimp_w)dbox_syshandle(d),CD_AUTHOR,i,0xff000040);
	}

	/* was this list the result of a search on the text of articles ? */
	if(fr->search_text_result)
	{
		t->cursor_index--;
		text_find_string_next(t,search_text_string_open,search_case_sensitive_open,
							  search_regex_flag_open);   /* highlight the string in the text */
		t->cursor_index++;
	}

	card_last_displayed2 = card_last_displayed;
	card_last_displayed = cptr;

	visdelay2_end();

	if((cptr->status & STATUS_MASK2) == STATUS_DRAFT)
	{
		edit_reply(t,news_mail+0x3030);
		text_close_window(t);
	}

	return(0);
}   /* end of card_edit */



void show_card_last_displayed()
/*****************************/
{
	if(card_last_displayed2 != NULL)
		article_view2(current_fr,-1,card_last_displayed2);
}   /* end of show_card_last_displayed */




void skip_thread(FOLDREC *fr, int action)
/***************************************/
/* Must be called immediately before card_open_next */
{
	int  ix;
	int  start;
	int  end;
	CARD *cptr;
	int  status;
	int  count;
	int  total;
	int  deleted=0;
	int  any_changed=0;
	int  changed_box = -2;
	int  box;
	int  this_level;

	if(check_lock_lists(0xffff) != 0)
		return;

	if(fr->ixlist == NULL)
		return;

	if(action == -1)
		action = options.skip_thread;

	if(action == 0)
		return;

	if(fr->display_levels < 1)
		return;   /* display has no threads */

	start = ix = fr->open_article_ref;

	if(fr->threading_done)
	{
		this_level = (fr->ixlist[ix] >> IXLEV) + 1;
		if(this_level < fr->display_levels)
			this_level = fr->display_levels;
	}
	else
	{
		this_level = fr->display_levels;
	}

	while((fr->ixlist[start] >> IXLEV) >= this_level)
		start--;

	end = ix+1;
	while((end < fr->n_entries) && ((fr->ixlist[end] >> IXLEV) >= this_level))
		end++;

	ix = start;
	total = end-start;
	for(count=0; count<total; count++,ix++)
	{
		cptr = cardfile_cptr(fr,ix);
		status = cptr->status & STATUS_MASK;

		if(action == 2)
		{
			if((status <= STATUS_UNREAD) || (status == STATUS_READ))
			{
				/* delete all unlocked articles in the thread */
				card_add_binned_list(cptr);
				cardfile_remove2(fr,cptr,BOX_BIN);
				deleted++;
				ix--;   /* because we've deleted one and moved the others up */
			}
		}
		else
		{
			if(status <= STATUS_UNREAD)
			{
				cptr->status = (cptr->status & ~STATUS_MASK) + STATUS_READ;
				article_update(fr,cptr,NULL,0,0);

				/* decrement unread count */
				if(boxes_unread_inc(box = (cptr->date_box >> 26),-1))
					set_boxlist_extent(7);

				if(box != changed_box)
				{
					if(changed_box == -2)
						changed_box = box;
					else
						changed_box = -1;   /* more than box changed */
				}
				any_changed = 1;
			}
		}
	}
	if(action == 2)
	{
		/* move to immediately before next thread */
		fr->open_article_ref = end - deleted - 1;
	}

	if(any_changed)
	{
		redraw_list_lines(fr,0);
		redraw_boxes_list(-1);
	}
}   /* end of skip_thread */





int card_open_next(FOLDREC *fr, int control, int open, int deleted)
/*****************************************************************/
/* Open the next or previous card */
/* control bit 0  1=forwards, 0=backwards
           bit 1  1=next unread article
           bit 2  1=don't beep
           bit 3  1=skip to start of next thread
           bit 16 1= already done thread sort
           bit 31 1= bits 0-23 are the ref of the new article
   deleted: open article has been removed from the list
*/
{
	int ix;
	int select_bit;
	int lev;
	int linenum;
	int forwards;
	int unread;
	int next_thread;
	int this_level;
	int start;
	int  y;
	int  i;
	int  lap_counter=0;
	int  old_linenum;
	int  end_linenum;
	int vector[N_FOLD_LEVELS];
	int old_open_vect[N_FOLD_LEVELS];
	CARD *cptr;
	CARD *cptr_prev;
	int  open_ref;
	int  new_ref;

	if(control & 0x80000000)
	{
		new_ref = control & 0xffffff;
		control = 0x80000000;
	}

	if((fr == NULL) || (fr == &list_fr[0]) || (fr->ixlist == NULL))
	{
		if((control & 4) == 0)
			beep();
		return(1);
	}

	i = control;
	if(fr->display->direction==1)
		i ^= 1;   /* swap direction if specified for this sorting type */

	if(i & 1)
		forwards = 1;
	else
		forwards = -1;

	unread = control & 2;
	next_thread = control & 8;

	if(control & 0x80000000)
	{
		ix = new_ref;
	}
	else
	{
		ix = fr->open_article_ref;
		if((deleted) && (forwards < 0))
			ix++;

		if(fr->threading_done)
		{
			this_level = (fr->ixlist[ix] >> IXLEV) + 1;
			if(this_level < fr->display_levels)
				this_level = fr->display_levels;
		}
		else
		{
			this_level = fr->display_levels;
		}

		/* find next article, or next unread article */
		for(;;)
		{
			ix += forwards;

			if(unread)
			{
				/* wrap round */
				if(ix < 0)
				{
					ix = fr->n_entries-1;
					lap_counter++;
				}
				if(ix >= fr->n_entries)
				{
					ix = 0;
					lap_counter++;
				}
				if(lap_counter > 1)
					ix = -1;   /* force end of loop */
			}

			if((ix < 0) || (ix >= fr->n_entries) || ((ix==fr->open_article_ref) && (deleted==0)))
			{
				if((control & 4) == 0)
					beep();

				return(1);   /* reached end of list */
			}

			if(next_thread)
			{
				if((fr->ixlist[ix] >> IXLEV) >= this_level)
					continue;    /* skip to start of next thread */
				else
					next_thread = 0;   /* reached start of next thread */
			}

			cptr = (CARD *)((fr->ixlist[ix] & IXLIST_MASK) << 2);

			if(unread == 0) break;

			if(((cptr->status & STATUS_MASK) <= STATUS_UNREAD) &&
					((cptr->status & STATUS_MASK2) != STATUS_FETCHING)) break;
		}
	}

	if(ix < 0)
		ix = 0;

	open_ref = fr->open_article_ref;
	cptr = (CARD *)((fr->ixlist[ix] & IXLIST_MASK) << 2);

	cptr_prev = (CARD *)((fr->ixlist[open_ref] & IXLIST_MASK) << 2);
	if((fr->display->threading) && ((control & 0x10000)==0))
	{
		if(cptr->source != fr->open_article_source)
		{
			/* find the start of this level */
			for(start=ix; start>0; start--)
			{
				if((fr->ixlist[start] >> IXLEV) == 0)
					break;
			}
			thread_sort(fr,start,0);

			if((control & 0x80000000) == 0)
			{
				return(card_open_next(fr,control | 0x10000,open,deleted));
			}
			else
			{
				/* find the new index for the required card */
				for(ix=start; ix>fr->n_entries; ix++)
				{
					if(cptr == (CARD *)((fr->ixlist[ix] & IXLIST_MASK) << 2))
						break;
				}
			}
		}
	}

	fr->open_article_ref = ix;

	if(open)
	{
		if(card_edit(text_view,fr,cptr,2,ix) != 0)     /* don't need to pass linenum, we redraw in here */
		{
			fr->open_article_ref = open_ref;
			return(0);
		}
	}
	ix = fr->open_article_ref;  /* may have been changed in card_edit
                                  by crosspost_remove */

	fr->open_article_source = cptr->source;

	list_selection_clear(fr);   /* clear any selection */
	/*    list_fade_icons(fr,wimp_INOSELECT);  */  /* not needed, call list_set_select later */

	lev = fr->display_levels;
	select_bit = (lev+1) << IXSEL;
	list_set_select(fr,ix,select_bit);

	selects_level = lev;
	selects_count = 1;

	linenum = fold_ref_to_line(fr,ix,lev);

	if(linenum < 0)
	{
		memcpy(old_open_vect,fr->open_vect,sizeof(old_open_vect));

		fold_ref_to_vector(fr,ix,lev,vector);
		fold_open_at_vector(fr,vector,lev-1);
		linenum = fold_ref_to_line(fr,ix,lev);
		list_set_title(fr);

		for(i=0; i<fr->open_levels; i++)
		{
			if(old_open_vect[i] != fr->open_vect[i])
			{
				break;
			}
		}
		old_linenum = fold_vector_to_line(fr,old_open_vect,i);

		if(linenum < old_linenum)
			old_linenum = linenum;

		i = fold_vector_to_line(fr,fr->open_vect,fr->open_levels);
		if(i < old_linenum)
			old_linenum = 1;

		set_list_extent(fr,1, 0);
		redraw_list_lines(fr,old_linenum);
	}
	else
	{
		redraw_list_oneline(fr,linenum);
	}

	/* moving forwards, try and show up to end of thread */
	memcpy(vector,fr->open_vect,sizeof(vector));
	vector[fr->open_levels] = fr->n_open[fr->open_levels];
	end_linenum = fold_vector_to_line(fr,vector,fr->open_levels);

	y = -(end_linenum+1) * lists_line_height;
	scroll_to_y(fr->window,y);

	scroll_to_y(fr->window,-(linenum-4) * lists_line_height);
	return(0);
}   /* end of card_open_next */




/*************************************************************************/
/*************************************************************************/


int list_set_colour(int colr, int not_selected,int x, int y)
/**********************************************************/
{
	int  h;
	int  reverse_colr;
	unsigned int  colour;
	unsigned int  backg;

	if(!not_selected)
	{
		reverse_colr = 1;

		if((colr & 0xf)==COLR_TAB_GREY)
		{
			/* white-on-grey */
			os_swi6(0x40743,0x77777700,0,0,0,0,0);
			os_swi4(0x4074f,0,0x77777700,WHITE,8);  /* ColourTrans_SetFonTColours */
		}
		else
		{
			os_swi6(0x40743,0,0,0,0,0,0);  /* SetGCOL - black */
			os_swi4(0x4074f,0,0x55555500,WHITE,8);  /* ColourTrans_SetFonTColours */
		}

		h = lists_line_height;
		bbc_rectanglefill(x-2,y+lists_font_offset-lists_line_height,2560,h-2);
		set_colour(WHITE,0,0);
	}
	else
	{
		reverse_colr = 0;

		colour = colr_tab[colr & 0xf];
		if(colr & 0x8000)
			backg = alist_backg_rgb;
		else
			backg = list_backg_rgb;

		os_swi4(0x4074f,0,backg,colour,14);  /* ColourTrans_SetFonTColours */
		set_colour(colour,0,0);
	}
	return(reverse_colr);
}   /* end of list_set_colour */




void display_list_lines(FOLDREC *fr, int start_line, int n_lines, wimp_redrawstr *r)
/**********************************************************************************/
{
	int  ix2;
	int  x, y;
	int  count;
	int  level;
	int  max_line;
	int  ix;
	CARD *cp;
	int  dependents;
	int  selected;
	int  box;
	unsigned int  colr;
	unsigned int  colr2;
	int  lev;
	static int reverse_colr = 0;

	x = r->box.x0 - r->scx + 2;
	y = r->box.y1 - r->scy - (start_line * lists_line_height);
	y -= lists_font_offset;

	if(options.colours[COLR_ALIST] != 0)
		colr = 0xdddddd00;   /* light grey */
	else
		colr = 0xffffff00;   /* white */

	os_swi6(0x40743,colr,0,0,0,0,0);  /* SetGCOL - light grey */
	bbc_rectanglefill(r->g.x0,r->g.y0,r->g.x1-r->g.x0, r->g.y1-r->g.y0);
	max_line = fold_n_lines(fr,0) - 1;

	ix = fold_line_to_ref(fr, start_line, &level, NULL);
	if(ix < 0) return;

	reverse_colr = 0;
	os_swi4(0x4074f,0,0xffffff00,0x00000000,14);  /* ColourTrans_SetFonTColours */

	for(count=0; count<n_lines; count++)
	{
		if(start_line > max_line) break;


		/* set 'dependents' to '*' if there are lower levels */
		dependents = ' ';


		/* write to the screen */
		selected = (fr->ixlist[ix] >> IXSEL) & IXSELB;

		/* colour this level if all IC or OG or header-only */
		for(ix2 = ix; ix2<fr->n_entries; ix2++)
		{
			if(ix2 != ix)
			{
				lev = fr->ixlist[ix2] >> IXLEV;
				if(lev > fr->display_levels)
					lev = fr->display_levels;

				if(lev <= level)
					break;
			}

			colr2 = 0;

			cp = (CARD *)((fr->ixlist[ix2] & IXLIST_MASK) << 2);
			box = cp->date_box >> 26;
			if(cp->status & STATUS_BIT_OG)
				colr2 = box_table[box].colour_og;
			else
				colr2 = box_table[box].colour_ic;

			if(cp->status & STATUS_BIT_HDR_ONLY)
				colr2 = COLR_TAB_GREY;

			if(ix == ix2)
			{
				colr = colr2;
				if(colr == 0)
					break;
			}
			else
			{
				if(colr != colr2)
				{
					colr = 0;
					break;
				}
			}
		}

		reverse_colr = list_set_colour(colr + 0x8000, level - (selected - 1), x,y);

		cp = (CARD *)((fr->ixlist[ix] & IXLIST_MASK) << 2);

		display_line(fr, x, y, cp, level, ix, r, reverse_colr, (cp->alength >> 24) & 7, colr);  // alength bits 24-26 is the article colour

		y -= lists_line_height;

		ix = fold_next(fr, ix, &level);
		if((ix < 0) || (level < 0))
			break;

		start_line++;
	}
}   /* end of display_list_lines */









void display_list_window(wimp_redrawstr *r, FOLDREC *fr)
/******************************************************/
{
	int  min_y;
	int  max_y;

	int  line;
	int  end_line;

	min_y = (r->box.y1 - r->g.y1) - r->scy;
	max_y = (r->box.y1 - r->g.y0) - r->scy;


	line = min_y / lists_line_height;
	end_line = max_y / lists_line_height;

	display_list_lines(fr,line,end_line - line + 1,r);

}   /* display_list_window */








void scroll_by(FOLDREC *fr,wimp_w w, int by)
/*****************************************/
{
	wimp_wstate wstate;

	wimp_get_wind_state(w,&wstate);
	wstate.o.y += by;
	if(wstate.o.y > 0)
		wstate.o.y = 0;

	open_list_window(&wstate.o,fr);
}   /* end of scroll_by */





void list_toggle_unread(FOLDREC *fr, int click)
/*********************************************/
{
	int  ix;
	unsigned int *ixlist;
	CARD *cptr;
	int  found;
	int  open_level;
	int  up_ix;
	int  down_ix;
	int  card;
	int  start_ix;
	int  card_open;

	int vector[N_FOLD_LEVELS];

	ixlist = fr->ixlist;
	open_level = fr->open_levels-1;

	card_open = (ixlist[fr->open_article_ref] & IXLIST_MASK) << 2;

	fr->hide_read ^= 1;
	if((fr->hide_read != 0) && (click == wimp_BRIGHT))
		hide_level = STATUS_LOCKED;
	else
		hide_level = STATUS_READ;


	ix = fold_vector_to_ref(fr,fr->open_vect,open_level);
	start_ix = ix;

	if((fr->hide_read != 0) && (fr->n_entries > 0))
	{
		/* previously fully expanded */
		down_ix = ix;
		up_ix = ix;

		found = -1;
		open_level++;
		while((found < 0) && (open_level>=0))
		{
			open_level--;

			/* search up for unread item until end of level */
			while(up_ix >= 0)
			{
				/* is this item unread ? */
				cptr = (CARD *)((ixlist[up_ix] & IXLIST_MASK) << 2);
				if((cptr->status & STATUS_MASK) < hide_level)
				{
					found = up_ix;
					break;
				}

				if((ixlist[up_ix] >> IXLEV) <= open_level)
					break;   /* reached start of level */

				up_ix--;
			}

			if(found >= 0) break;

			/* search down for unread item until end of level */
			while(++down_ix < fr->n_entries)
			{
				if((ixlist[down_ix] >> IXLEV) <= open_level)
					break;    /* reached end of level */

				/* is this item unread ? */
				cptr = (CARD *)((ixlist[down_ix] & IXLIST_MASK) << 2);
				if((cptr->status & STATUS_MASK) >= hide_level)
				{
					found = down_ix;
					break;
				}
			}
		}

		if(found >= 0)
			ix = found;
		else
			ix = 0;
		start_ix = 0;
	}

	if(ix >= 0)
		card = (ixlist[ix] & IXLIST_MASK) << 2;

	list_sort(fr,0);

	/* now find this card again */
	if(ix >= 0)
	{
		for(ix=start_ix; ix<fr->n_entries; ix++)
		{
			if(card == ((ixlist[ix] & IXLIST_MASK) << 2))
			{
				list_set_title(fr);

				if((open_level >= 0) && (fr->display->threading) && (fr->threading_done==0))
				{
					/* find the start of this newsgroup */
					for(start_ix=ix; start_ix >= 0; start_ix--)
					{
						if((ixlist[start_ix] >> IXLEV) == 0)
							break;
					}
					thread_sort(fr,start_ix,0);

					/* now find this card again */
					for(ix=start_ix; ix<fr->n_entries; ix++)
					{
						if(card == ((ixlist[ix] & IXLIST_MASK) << 2))
							break;
					}
				}

				fold_ref_to_vector(fr,ix,open_level,vector);
				fold_open_at_vector(fr,vector,open_level);
				break;
			}
		}
	}

	/* find the currently open article again */
	fr->open_article_ref = 0;
	for(ix=0; ix<fr->n_entries; ix++)
	{
		if(card_open == ((ixlist[ix] & IXLIST_MASK) << 2))
		{
			fr->open_article_ref = ix;
			break;
		}
	}

	set_icon_state(fr->window_buttons,6,fr->hide_read ^ 1);
	list_fade_icons(fr,wimp_INOSELECT);
	list_open_window(fr);
}   /* end of list_toggle_unread */





void close_list_window(FOLDREC *fr)
/*********************************/
{

	if(fr != &list_fr[0])
	{
		/* subset window, delete it */
		list_delete_window(fr);
	}
	else
	{
		wimp_close_wind(fr->window);
	}
}   /* end of close_list_window */





void close_all_list_windows(int cf)
/*********************************/
/* Close all list windows which contain articles with this cf
   cf = -1 means all */
{
	int  ix;
	int  list;
	unsigned int *ixlist;
	FOLDREC *fr;

	for(list=1; list<N_LISTS; list++)
	{
		fr = &list_fr[list];

		if((fr->window != NULL) && ((ixlist = fr->ixlist) != NULL))
		{
			for(ix=0; ix<fr->n_entries; ix++)
			{
				if(cptr_to_cf((CARD *)((ixlist[ix] & IXLIST_MASK) << 2)) == cf)
					break;
			}
			if(ix==fr->n_entries)
			{
				/* no articles in this window to match cf */
				continue;
			}
		}

		close_list_window(fr);
	}
}   /* end of close_all_list_windows */





void selected_button_box(int *hits)
/*********************************/
{
	/* change the box number of the selected articles */
	int  box;

	box = box_lookup_number(hits[0]);
	if((box < BOX_BIN) && (copy_box))
		list_selection_delete(current_fr,box,1);
	else
		list_selection_delete(current_fr,box,0);

	/*   list_set_status(current_fr,hits[0]+0x100);   */
}   /* end of button_box_selected */



void selected_display(int *hits)
/******************************/
{
	display_called(hits[0]+2,current_fr,0);
	list_open_window(current_fr);
	dbox_setfield(current_fr->dbox_list,7,current_fr->display->name);
}   /* end of selected display */





void key_list_window(wimp_eventdata *d, FOLDREC *fr)
/**************************************************/
{
	int  c;
	int  shiftkey;
	int  user;
	int  menu_type;

	shiftkey = akbd_pollsh() << 1;
	c = d->key.chcode;

	switch(c)
	{
	case '4':
		if(is_keypressed(122))
			c = 16;   /* CTRL-P */
		break;

	case '6':
		if(is_keypressed(26))
			c = 14;   /* CTRL-N */
		break;

	case '+':
		if(is_keypressed(58))
			c = 20;   /* CTRL-T */
		break;
	}

	switch(c)
	{
	case KEY_UP:
		/* shouldn't open and close levels ???? */
		card_open_next(fr,0,0,0);
		break;

	case KEY_DOWN:
		card_open_next(fr,1,0,0);
		break;

	case PAGE_UP:
		scroll_page(d->c.w,1);
		break;

	case PAGE_DOWN:
		scroll_page(d->c.w,-1);
		break;

	case KEY_CT_DOWN:
		scroll_to_top(d->c.w,1);
		break;

	case KEY_CT_UP:
	case KEY_HOME:
		scroll_to_top(d->c.w,-1);
		break;

	case F_KEY_CTRL+2:
		close_list_window(fr);
		set_boxlist_extent(6);
		break;

	case F_KEY+3:
		list_export_articles(fr,0);
		break;

	case F_KEY_CTRL+3:
		list_export_multipart(fr,0);
		break;

	case F_KEY+4:   /* find */
		subset_dialogue(fr,0);
		break;

	case F_KEY+6:   /* sort */
	case F_KEY_SHIFT+6:
		current_fr = fr;
		if(c == F_KEY_SHIFT+6)
		{
			menu_type = 0x2000;
			make_boxes_menu(fr->box + 0x800 + menu_type,NULL);  /* allow copy to this same box */
			copy_box=1;
		}
		else
		{
			menu_type = 0x1000;
			make_boxes_menu(fr->box + 0x900 + menu_type,NULL);  /* don't allow move to this same box */
			copy_box=0;
		}
		dbox_menu(wmenu_boxes_names,selected_button_box,NULL);
		break;

	case F_KEY+7:
		addrlist_open(1);
		break;

	case 01:   /* ctrl A */
		list_select_all_open(fr);
		break;

	case 02:   /* ctrl B */
		if(akbd_pollsh())
			list_fetch_full(fr,1);   /* cancel fetch */
		else
			list_fetch_full(fr,0);
		break;

	case 05:   /* ctrl E */
		list_set_status(fr,10);   /* unread->read */
		break;

	case 8:   /* CTRL H */
		if(akbd_pollctl())
		{
			list_toggle_unread(fr,wimp_BLEFT);
		}
		break;

	case 9:   /* CTRL I */
		thread_change_offset(fr,akbd_pollsh());
		break;

	case 11:   /* ctrl K */
		list_selection_delete(fr,BOX_BIN,0);   /* same as CTRL-X */
		break;

	case 12:   /* ctrl L */
		list_set_status(fr,STATUS_LOCKED);    /* set marked */
		break;

	case 13:   /* ctrl M */
		if(akbd_pollctl())   /* distinguish ctrl-m from c.return */
			list_set_status(fr,STATUS_MARKED);
		break;

	case 14:   /* ctrl N */
		/* problem if text window hasn't been opened before  ???? */
		card_open_next(fr,1+shiftkey,1,0);
		break;

	case 15:   /* ctrl O */
		if(akbd_pollsh())
			list_close_level(fr);
		list_set_title(fr);
		break;

	case 16:   /* ctrl P */
		card_open_next(fr,0+shiftkey,1,0);
		break;

	case 18:   /* ctrl R */
		break;

	case 20:   /* ctrl T */
		if(fr->display_levels > 1)
		{
			skip_thread(fr,-1);
			card_open_next(fr,9+shiftkey,1,0);
		}
		break;

	case 21:   /* ctrl U */
		list_set_status(fr,STATUS_UNREAD);    /* set marked */
		break;

	case 23:    /* ctrl W, write, create blank article window */
		user = 0;
		if(fr->box < BOX_BIN)
			user = box_table[fr->box].default_user;
		new_reply(fr,NULL,NULL,fr,user,NULL);
		break;

	case 24:   /* ctrl X */
		list_selection_delete(fr,BOX_BIN,0);
		break;

	case 26:   /* ctrl Z */
		list_selection_clear(fr);
		list_fade_icons(fr,wimp_INOSELECT);
		redraw_list_lines(fr,0);
		break;

	case 27:   /* ESCAPE */
		list_escape_flag = 1;
		break;

	default:
		wimp_processkey(c);
		break;
	}

}   /* end of key_list_window */



static int drag_last_line = -1;
static int drag_last_ref = -1;
static int drag_level_top = 0;
static int drag_level_end = 0;



void list_drag_continue(FOLDREC *fr, int open, wimp_wstate *wstate, wimp_wstate *wstate1, wimp_mousestr *m)
/*****************************************************************************************************/
{
	int  y;
	int  i;
	int  linenum;
	int  ref;
	int  lev;
	int  lev2;
	int  start;
	int  end;
	int  start_line;
	int  end_line;
	int  select;
	int  select_adj;
	int  level;
	int  adjust;

	if(fr == NULL)
		return;

	adjust = m->bbits & 0x111;
	adjust=0;

	/* any scrolling to be done ? */
	if(open)
	{
		wimp_open_wind(&wstate->o);
	}


	y = m->y;      /* screen coord in click */
	i = wstate->o.box.y1 - y;   /* displacement down the window */
	y = i - wstate->o.y;        /* work area coordinate */

	/* find article from workarea coords */
	linenum = y/lists_line_height;

	if(linenum == drag_last_line)
		return;

	level = fr->cursor_level;

	ref = fold_line_to_ref(fr,linenum,&lev,fr->cursor_vect);

	select = (level+1) << IXSEL;
	select_adj = select;
	i = (ref - fr->cursor_ref) * (drag_last_ref - ref);
	if(i >= 0)
		select = 0;

	if(drag_last_ref > ref)
	{
		start = ref;
		if(select == 0)
			start++;
		start_line = linenum;
		end = drag_last_ref;
		end_line = drag_last_line;
	}
	else
	{
		start = drag_last_ref;

		start_line = drag_last_line;
		end = ref;
		if(select == 0)
			end--;
		end_line = linenum;
	}


	for(i=start; i<=end; i++)
	{
		if(i < drag_level_top)
			continue;

		if(i >= drag_level_end)
			break;

		lev = fr->ixlist[i] >> IXLEV;
		if(lev > fr->display_levels)
			lev = fr->display_levels;

		if((i == drag_level_top) || (lev == level))
		{
			if(adjust)
			{
				if((fr->ixlist[i] & IXLIST_SELECTED))
					list_set_select(fr,i,0);
				else
					list_set_select(fr,i,select_adj);
			}
			else
				list_set_select(fr,i,select);
		}
	}

	lev = fr->ixlist[start] >> IXLEV;
	if(lev > fr->display_levels)
		lev = fr->display_levels;

	lev2 = fr->ixlist[end] >> IXLEV;
	if(lev2 > fr->display_levels)
		lev2 = fr->display_levels;

	if((start == drag_level_top) || (lev == level) || (lev2 == level))
		redraw_list_line_range(fr,start_line,end_line);

	drag_last_ref = ref;
	drag_last_line = linenum;
}   /* end of list_drag_continue */



void article_view_external(FOLDREC *fr, int ref)
/**********************************************/
{
	CARD *cptr;
	char *fname_ole;
	FILE *f;
	TEXTR *t;
	CARD_EXPANDED cex;

	if(ref < 0)
		return;

	t = &text_rec_temp;
	cptr = (CARD *)((fr->ixlist[ref] & IXLIST_MASK) << 2);

	if(check_lock_lists(0xffff) != 0)
		return;

	unpack_card(cptr, &cex);
	if(article_read(t,cex.addr,cex.docbox,cex.text_offset,cex.text_length,0,0,1) < 0)
	{
		beep();
		return;
	}

	fname_ole = get_temp_fname();
	f = fopen_werr(fname_ole,"w",NULL);
	if(f==NULL)
		return;

	fwrite(t->text_base+t->text_start,1,t->text_length,f);
	fclose(f);

	OLE_start(fname_ole,0xfff,3,fr->window,0,(void *)fr);

	flex_free((flex_ptr)&t->text_base);
	t->text_base = NULL;
	t->text_buf_size = 0;
}   /* end of article_view_external */





void article_view2(FOLDREC *fr, int ref, CARD *cptr)
/**************************************************/
/* View in an secondary article viewer */
{
	TEXTR *t;
	CARD_EXPANDED cex;

	if(cptr == NULL)
		cptr = (CARD *)((fr->ixlist[ref] & IXLIST_MASK) << 2);

	unpack_card(cptr, &cex);

	t = text_data_init(cex.text_length,X_VIEW2);
	if(t == NULL)
		return;

	if(card_edit(t,fr,cptr,0,ref) != 0)
	{
		beep();
		text_data_free(t);
		return;
	}

}   /* end of article_view2 */




void article_view3(TEXTR *t, CARD *cptr)
/**************************************/
/* Open a new article in the same article viewer */
{
	int  ix;
	FOLDREC *fr;
	int  found=0;

	if((t == text_view) && ((fr = t->fr) != NULL) && (fr->ixlist != NULL))
	{
		/* is the new cptr in this list ? */
		for(ix=0; ix<fr->n_entries; ix++)
		{
			if((CARD *)((fr->ixlist[ix] & IXLIST_MASK) << 2) == cptr)
			{
				/* found the article in the list, select it */
				found = 1;
				break;
			}
		}
	}

	if((akbd_pollsh()) || ((t == text_view) && (found==0)))
	{
		/* change to an additional article viewer */
		article_view2(NULL,-1,cptr);
	}
	else
	{
		if(t == text_view)
		{
			card_open_next(fr,ix+0x80000000,1,0);
		}
		else
		{
			if(card_edit(t,fr,cptr,0,-1) != 0)
				beep();
		}
	}
}   /* end of article_view3 */




void iconise_aviewer(int window)
/******************************/
/* Convert primary article viewer to secondary article viewer */
{

	if(window == text_view->w_card)
	{
		text_view_iconised = text_view;
	}

}   /* end of iconise_aviewer */





int list_get_linenum(wimp_w window)
/*********************************/
{
	int  i;
	int  y;
	int  linenum;

	wimp_eventstr *e;
	wimp_eventdata *d;
	wimp_wstate wstate;

	e = wimpt_last_event();
	d = &e->data;

	wimp_get_wind_state(window,&wstate);
	i = wstate.o.box.y1 - d->but.m.y;   /* displacement down the window */
	y = i - wstate.o.y;        /* work area coordinate */

	linenum = y/lists_line_height;

	return(linenum);
}   /* end of list_get_linenum */





void click_list_window(wimp_eventdata *d, FOLDREC *fr)
/****************************************************/
{
	int  i;
	int  article;
	int  lev;
	int  linenum;
	int  prev_linenum;
	int  n_open;
	int  select_bit;
	int  y_bottom;
	int  y_top;
	int  open_article=0;
	int  first_article;
	CARD *cptr;
	static int click_line = 0;

	set_focus(fr->window);

	/* find article from workarea coords */
	linenum = list_get_linenum(fr->window);
	article = fold_line_to_ref(fr,linenum,&lev,fr->cursor_vect);
	select_bit = (lev+1) << IXSEL;


	/* mark previous article as read */
	card_set_read(fr);

	switch(d->but.m.bbits)
	{
	case wimp_BRIGHT:        /* adjust, 2nd click */
		if(click_line != linenum)
			break;

		if(lev==fr->display_levels)
		{
			/* adjust-double-click on article, use additional article viewer */
			article_view2(fr,article,NULL);
			return;   /* don't de-select the article in the article list */
		}

		/* drop through */
	case wimp_BLEFT:   /* double click */
		if(click_line != linenum)
			break;

		if((lev==fr->display_levels) && akbd_pollsh())
		{
			/* shift-double-click on article, view with external editor */
			article_view_external(fr,article);
			break;
		}

		if((lev==fr->display_levels) ||
				((lev >= (fr->display_levels-1)) && (options.thread_open > 0)  && !dbox_persist()))
		{
			if(card_check_changed(text_view)!=0)
				break;

			/* open at the lowest level, display article */
			open_article = 1;
		}
		else
		{
			/*         close_article_window(text_view);  */
		}


		if(lev < fr->display_levels)
		{
			if(akbd_pollsh())
			{
				/* shift-double-click, create subset list */
				if(d->but.m.bbits == wimp_BRIGHT)
					article = -1;   /* make it look for any selected groups */
				subset_expand_level(fr,article,lev);
				break;
			}

			if((i = fold_is_line_open(fr,linenum)) < 0)
			{
				list_set_select(fr,article,0);

				/* how many lines are open at the level of the current line and above it ? */
				prev_linenum = fold_linenum_open(fr,lev);
				n_open = fold_n_lines(fr,lev+1);


				if(fr->display->threading)
				{
					if(lev==0)
					{
						thread_sort(fr,article,0);
					}
					else if((lev==0) && (fr->threading_done==0))
					{
						fold_open_at_line(fr,linenum);
						thread_sort(fr,fr->open_ref[0],0);
					}
				}

				fold_open_at_line(fr,linenum);
				list_set_title(fr);

				if((n_open > 0) && (prev_linenum < linenum))
				{
					linenum = prev_linenum;
				}
				redraw_list_lines(fr,linenum);
				set_list_extent(fr,2, linenum);

				/* get the whole of the opened level in the window */
				y_top = fold_vector_to_line(fr,fr->open_vect,lev);
				y_bottom = y_top + fr->n_open[lev+1] + lev + 1;
				scroll_to_y(fr->window,-y_bottom * lists_line_height);   /* bottom of the opened level */
				scroll_to_y(fr->window,-y_top * lists_line_height);      /* top of opened level */


			}
			else
			{
				redraw_list_lines(fr,linenum);
				fold_close_level(fr,i);
				set_list_extent(fr,1, linenum);
				open_article = 0;
			}
		}

		if((open_article) && (article >= 0))
		{
			if((lev < fr->display_levels) && (options.thread_open >= 2))
			{
				/* open first unread article in this thread */
				/*  open_article = 0;  */ /* don't open any unless there is an unread article */
				first_article = article;
				for(;;)
				{
					cptr = (CARD *)((fr->ixlist[article] & IXLIST_MASK) << 2);
					if((cptr->status & STATUS_MASK) <= STATUS_UNREAD)
					{
						open_article = 1;
						if(options.thread_open == 2)
							break;   /* 1st unread */
						else
							first_article = article;  /* last unread */
					}

					if((++article >= fr->n_entries) || ((fr->ixlist[article] >> IXLEV) < fr->display_levels))
					{
						article = first_article;
						break;   /* end of thread */
					}
				}
				/*
				This doesn;t work properly.
				   y = fold_ref_to_line(fr,article,lev+1);
				   y = -(y+1) * lists_line_height;
				   scroll_to_y2(fr->window,y,y-lists_line_height);
				*/
			}

			if(open_article)
			{
				cptr = (CARD *)((fr->ixlist[article] & IXLIST_MASK) << 2);

				list_selection_clear(fr);   /* clear any selection */
				selects_count = 1;
				selects_level = fr->display_levels;
				list_set_select(fr,article,(selects_level+1) << IXSEL);

				if(card_edit(text_view,fr,cptr,0,article) == 0)
				{
					fr->open_article_ref = article;
					fr->open_article_source = cptr->source;
				}
			}
		}
		return;
	}

	/* single click, or second-click on different line */
	switch(d->but.m.bbits)
	{
	case wimp_BLEFT:
	case wimp_BCLICKLEFT:    /* select */

		list_selection_clear(fr);   /* clear any selection */

		click_line = linenum;
		fr->cursor_ref = article;
		fr->cursor_level = lev;

		drag_last_line = linenum;
		drag_last_ref = article;

		list_set_select(fr,article,select_bit);

		selects_level = lev;
		selects_count = 1;
		redraw_list_oneline(fr,linenum);
		break;

	case wimp_BRIGHT:
	case wimp_BCLICKRIGHT:   /* adjust */
		click_line = linenum;

		drag_last_line = linenum;
		drag_last_ref = article;


		redraw_list_oneline(fr,linenum);
		if((selects_count > 0) && (selects_level != lev))
		{
			/* selection at different level, de-select others */
			list_selection_clear(fr);
			selects_level = lev;
			selects_count = 1;
			list_set_select(fr,article,select_bit);
		}
		else if((article >= 0) && (fr->ixlist[article] & select_bit))
		{
			/* already selected */
			list_set_select(fr,article,0);
			selects_count--;
		}
		else
		{
			list_set_select(fr,article,select_bit);
			selects_count++;
		}
		break;

	case wimp_BDRAGRIGHT:
	case wimp_BDRAGLEFT:
		fr->cursor_ref = drag_last_ref;
		if(akbd_pollsh() || akbd_pollctl())
		{
			if(fr->cursor_level==0)
				drag_level_top = 0;
			else
				drag_level_top = fr->open_ref[fr->cursor_level-1];
			fold_count_at_level(fr,drag_level_top,fr->cursor_level,&drag_level_end);
			drag_start(&d->but.m,2,(void *)fr,fr->window);
		}
		break;

	default:

		break;
	}

}   /* end of click_list_window */




void open_list_window(wimp_openstr *o, FOLDREC *fr)
/*************************************************/
/* Open the text window, as specified.  Also open the
   button bar */
{
	int max_y;
	wimp_openstr b;
	wimp_wstate wstatet;

	/* make sure there's room for button bar above the text window */
	max_y = y_screen_dim - (list_button_bar_height + scroll_bar_height - 2);

	if(o->box.y1 > max_y)
		o->box.y1 = max_y;
	if(o->box.y0 < scroll_bar_height)
		o->box.y0 = scroll_bar_height;


	wimp_open_wind(o);     /* open list window */
	wimp_get_wind_state(o->w,&wstatet);

	b.box.x0 = wstatet.o.box.x0;
	b.box.x1 = wstatet.o.box.x1 + scroll_bar_width;
	b.box.y0 = wstatet.o.box.y0;
	b.box.y1 = wstatet.o.box.y1 + list_button_bar_height;
	b.x = 0;
	b.y = 0;
	b.w = fr->window_buttons;
	b.behind = o->w;
	wimp_open_wind(&b);    /* open button bar and title bar */
}   /* end of open_list_window */



void open_list_button_window(wimp_openstr *o, wimp_w window)
/**********************************************************/
/* Open the button bar window, as specified.  Also open the
    list window */
{
	int behind;
	int  top_window;
	wimp_w this_w;

	wimp_wstate wstate;
	wimp_wstate wstate_t;

	this_w = o->w;
	wimp_get_wind_state(window,&wstate_t);

	behind = top_window = o->behind;

	if(behind <= -2)
	{
		if(behind == -3)
			wimp_open_wind(o);

		wstate_t.o.behind = behind;
		wimp_open_wind(&wstate_t.o);

		/* put to back */
		if(behind == -2)
			wimp_open_wind(o);
		return;
	}


	if((behind == -1) && (wstate_t.o.behind == -1))
		o->behind = window;

	behind = o->behind;
	wimp_open_wind(o);   /* open button bar & title bar */

	wimp_get_wind_state(this_w,&wstate);

	wstate_t.o.box.x0 = wstate.o.box.x0;
	wstate_t.o.box.x1 = wstate.o.box.x1 - scroll_bar_width;
	wstate_t.o.box.y1 = wstate.o.box.y1 - list_button_bar_height;
	wstate_t.o.box.y0 = wstate.o.box.y0;

	if(behind == -1)
		wstate_t.o.behind = -1;
	else
		wstate_t.o.behind = top_window;   /* Added 28.6.99 */
	wimp_open_wind(&wstate_t.o);    /* open list window */

	wstate.o.behind = window;
	wimp_open_wind(&wstate.o);  /* reopen button window to ensure it's behind the list window */

}   /* end of open_list_button_window */




void list_window_handler(wimp_eventstr *e, void *handle)
/******************************************************/
{
	int  more;
	wimp_redrawstr r;
	FOLDREC *fr;

	fr = (FOLDREC *)handle;

	switch (e->e)
	{
	case wimp_ENULL:
		if(null_event_map & 1)
			speak_text_continue();

		if(null_event_map & 2)
			drag_continue();
		break;

	case wimp_EREDRAW:
		r.w = fr->window;
		wimp_redraw_wind(&r, &more);

		while(more)
		{
			/* display the text */
			display_list_window(&r,fr);
			wimp_get_rectangle(&r, &more);
		}
		break;

	case wimp_EOPEN:
		open_list_window(&e->data.o,fr);
		break;

	case wimp_ECLOSE:  /* Pass on close request */
		close_list_window(fr);
		break;

	case wimp_EBUT:
		click_list_window(&e->data,fr);
		break;

	case wimp_EKEY:
		key_list_window(&e->data,fr);
		break;

	case wimp_ESCROLL:
		scroll_window(&e->data);
		break;

	case wimp_EUSERDRAG:
		null_events(2,0);     /* turn off for drag */
		break;

	case wimp_EPTRLEAVE:
		visdelay2_leave();
		break;

	case wimp_EPTRENTER:
		visdelay2_enter();
		break;

	case wimp_ESEND:
	case wimp_ESENDWANTACK:
		switch(e->data.msg.hdr.action)
		{
		case wimp_MDATALOAD:     /* insert data */
		case wimp_MDATAOPEN:
			break;
		}
		break;

	default:   /* Ignore any other event */
		break;
	}
}   /* end of list_window_handler */




void click_button_window(wimp_eventdata *d, FOLDREC *fr)
/******************************************************/
/* Click on the button bar */
{
	int  icon;
	int  click;
	int  user;
	int  box;
	int  menu_type;
	wimp_mousestr *mouse;
	CARD *cptr;

	mouse = &d->but.m;
	icon = mouse->i;
	click = mouse->bbits;
	current_fr = fr;
	mouse = &d->but.m;

	switch(icon)
	{
	case 2:   /* delete */
		if(mouse->bbits == wimp_BMID)
		{
			card_make_binned_menu(fr);
			dbox_menu(wmenu_binned,card_binned_selected,mouse);
		}
		else
		{
			list_selection_delete(fr,BOX_BIN,0);
		}
		break;

	case 1:   /* change box */
		box = 255;
		if((fr->box >= 0) && (fr->box < BOX_BIN))
		{
			box = box_table[fr->box].archive_to;
		}

		if((mouse->bbits == wimp_BRIGHT) && (box != BOX_BIN) && (box != 255) && (box != fr->box))
		{
			list_selection_delete(current_fr,box,box_table[fr->box].flags & BOX_ARCHIVE_COPY);
		}
		else
		{

			if(akbd_pollsh())
			{
				copy_box=1;
				menu_type = 0x2000;
				make_boxes_menu(fr->box + 0x800 + menu_type,NULL);
			}
			else
			{
				copy_box=0;
				menu_type = 0x1000;
				make_boxes_menu(fr->box + 0x900 + menu_type,NULL);
			}
			dbox_menu(wmenu_boxes_names,selected_button_box,mouse);
		}
		break;

	case 8:    /* write, create blank article window */
		user = 0;
		if(fr->box < BOX_BIN)
			user = box_table[fr->box].default_user;

		cptr = cardfile_cptr(fr,0);

		if((dbox_persist()) || ((cptr != NULL) && (cptr->status & STATUS_BIT_NEWS)))
			new_reply(fr,NULL,"",fr,user,NULL);
		else
			new_reply(fr,NULL,NULL,fr,user,NULL);
		break;

	case 3:    /* export */
		list_export_articles(fr,0);
		break;

	case 4:    /* mark read */
		if(click == wimp_BRIGHT)
			list_set_status(fr,STATUS_UNREAD);
		else
			list_set_status(fr,STATUS_READ);
		break;

	case 5:    /* mark locked */
		if(click == wimp_BRIGHT)
			list_set_status(fr,STATUS_MARKED);
		else
			list_set_status(fr,STATUS_LOCKED);
		break;

	case 6:    /* hide unread articles */
		list_toggle_unread(fr,click);
		break;

	case 7:   /* display/sort type */
		if(check_lock_lists_fr(0xffff,fr) == 0)
			dbox_menu(wmenu_display,selected_display,mouse);
		break;

	case 9:  /* killfile */
		list_killfile_add(fr);
		break;
	}
}   /* end of click_button_window */




BOOL button_window_handler(dbox d, void *event, void *handle)
/***********************************************************/
{
	FOLDREC *fr;
	wimp_eventstr *e = event;

	fr = (FOLDREC *)handle;

	switch (e->e)
	{
	case wimp_EOPEN:
		open_list_button_window(&e->data.o,fr->window);
		return(TRUE);

	case wimp_ECLOSE:  /* Pass on close request */
		close_list_window(fr);   /* close list window also */
		if(dbox_persist())
			set_boxlist_extent(3);
		else
			set_boxlist_extent(6);
		break;

	case wimp_EPTRLEAVE:
		visdelay2_leave();
		break;

	case wimp_EPTRENTER:
		visdelay2_enter();
		break;


	case wimp_EBUT:
		click_button_window(&e->data,fr);
		return(TRUE);

	default:   /* Ignore any other event */
		break;
	}
	return(help_handler(event,HELP_LIST));
}   /* end of button_window_handler */



void dbox_list_handler(dbox d, void *handle)
/******************************************/
{
	FOLDREC *fr;

	fr = (FOLDREC *)handle;

}   /* end of dbox_list_handler */




menu list_menu_maker(void *handle)
/*********************************/
{
	int  ix;
	unsigned *ixlist;
	FOLDREC *fr;
	int  fade;
	int  found_ix;   /* set if 1 and only 1 item is selected */
	CARD *cptr;

	int  linenum;
	int  article;
	int  select_bit;
	int  lev;
	static menu list_menu_boxes = 0;

	fr = (FOLDREC *)handle;

	/* are there any selected items in this list */
	fade = 1;
	found_ix = -1;
	ixlist = fr->ixlist;

	for(ix=0; ix<fr->n_entries; ix++)
	{
		if(ixlist[ix] & IXLIST_SELECTED)
		{
			if(fade == 1)
			{
				/* first selected item to be found */
				if(((ixlist[ix] >> IXSEL) & IXSELB) > fr->display_levels)
					found_ix = ix;
			}
			else
			{
				found_ix = -1;
				break;
			}
		}
	}

	fade = 0;
	if(fr->n_selected == 0)
	{
		/* no selected lines. menu-click should select the current line */
		if((linenum = list_get_linenum(fr->window)) < fr->n_lines)
		{
			article = fold_line_to_ref(fr,linenum,&lev,fr->cursor_vect);
			select_bit = (lev+1) << IXSEL;

			found_ix = article;

			fr->cursor_ref = article;
			fr->cursor_level = lev;

			list_set_select(fr,article,select_bit);
			redraw_list_oneline(fr,linenum);
			blist_current_menu = menu_list;
			blist_current_menu_line = linenum;
			blist_current_menu_blist = (int)fr;
		}
		else
		{
			fade = 1;
		}
	}
	menu_setflags(menu_list,1,0,fade);
	menu_setflags(menu_list,2,0,fade);

	if(found_ix >= 0)
	{
		cptr = (CARD *)((ixlist[found_ix] & IXLIST_MASK) << 2);
		sprintf(menu_score_value,"%d",cptr->score);
	}
	else
	{
		menu_score_value[0] = 0;
	}

	if(!dbox_persist())
	{
		menu_submenu(menu_list,1,0);
		menu_submenu(menu_selection,11,0);
		menu_submenu(menu_selection,12,0);

		make_boxes_menu(0x0800,&list_menu_boxes);
		menu_submenu(menu_selection,11,list_menu_boxes);
		menu_submenu(menu_selection,12,list_menu_boxes);

		menu_submenu(menu_list,1,menu_selection);
	}
	return(menu_list);
}   /* end of list_menu_maker */




FOLDREC *list_new_window()
/************************/
{
	wimp_w  w;
	int  record;
	FOLDREC *fr;
	wimp_wind *window_template;
	template *t;
	dbox dbox_list;
	static int last=0;     /* allocate records cyclically */

	/* find free record */
	for(record=last+1; ; record++)
	{
		if(record>=N_LISTS) record=1;

		if(record == last)
		{
			werr(0,"No more Article List windows");
			return(NULL);
		}

		if(list_fr[record].window == NULL)
		{
			break;
		}
	}

	last = record;

	fr = &list_fr[record];
	memset(fr,0,sizeof(FOLDREC));

	/* main list window */
	t = template_copy(template_find("List"));
	window_template = &t->window;
	window_template->box.x0 = options.position_x[3];
	window_template->box.x1 = options.position_x[3] + 1300;
	window_template->box.y1 = options.position_y[3];

	if(options.colours[COLR_ALIST] != 0)
		window_template->colours[wimp_WCWKAREABACK] = 255;   /* transparent */


	wimp_create_wind(window_template ,&w);
	win_register_event_handler(w,list_window_handler,(void *)fr);
	memcpy(&fr->workarea.box,&window_template->ex,sizeof(wimp_box));

	event_attachmenumaker(w,list_menu_maker,list_menu_proc,(void *)fr);


	/* create button bar */
	dbox_list = dbox_new("ListBtns");
	dbox_raw_eventhandler(dbox_list,button_window_handler,(void *)fr);
	dbox_eventhandler(dbox_list, dbox_list_handler, (void *)fr);
	fr->window_buttons = dbox_syshandle(dbox_list);
	fr->dbox_list = dbox_list;


	/* NOTE: We could save the template pointers (t) in FR, so that
	   we can free them when the window is deleted */

	fr->window = w;
	fr->w_template = window_template;

	list_fade_icons(fr,wimp_INOSELECT);
	/*   list_open_window(fr); */
	return(fr);
}   /* end of list_new_window */




FOLDREC *subset_new(char *name,FOLDREC *parent,int count)
/*******************************************************/
{
	FOLDREC *fr;

	fr = list_new_window();
	if(fr == NULL)
		return(NULL);

	strcpy(fr->name,name);
	fr->parent = parent;
	fr->external_box = 0;


	fr->ixlist = malloc_dont_budge(count*sizeof(int));

	fr->n_entries = 0;
	fr->n_entries_tot = 0;

	if(fr->ixlist == NULL)
	{
		malloc_err_string(10,"for article list");
		fr->n_entries_max = 0;
		return(NULL);
	}
	fr->n_entries_max = count;
	fr->display = parent->display;
	fr->box = parent->box;
	fr->box_expansion = 0;

	win_settitle(fr->window_buttons,name);
	dbox_setfield(fr->dbox_list,7,fr->display->name);

	list_open_window(fr);
	set_focus(fr->window);
	return(fr);
}   /* end of subset_new */





/*********************************************************************************/

/*********************************************************************************/



void list_deselect(FOLDREC *fr,int linenum)
/*************************************/
{
	list_selection_clear(fr);
	list_fade_icons(fr,wimp_INOSELECT);
	redraw_list_oneline(fr,linenum);
}   /* end of list_deselect */




void list_menu_proc(void *handle, char *hit)
/*******************************************/
{
	FOLDREC *fr;
	int  box;
	int  copy_box;
	wimp_mousestr mouse;

	fr = (FOLDREC *)handle;
	copy_box = 1;

	switch(hit[0])
	{
	case 1:   /* Selection */
		switch(hit[1])
		{
		case 1:
			list_selection_clear(fr);
			list_fade_icons(fr,wimp_INOSELECT);
			redraw_list_lines(fr,0);
			return;

		case 2:
			list_selection_delete(fr,BOX_BIN,0);
			return;

		case 3:   /* remove attachments */
			list_remove_attachments(fr);
			break;

		case 4:   /* change subject */
			list_set_subject(fr,1);
			break;

		case 5:   /* change author */
			list_set_subject(fr,2);
			break;

		case 6:   /* change source */
			list_set_source(fr,0);
			return;

		case 7:
			if(hit[2]==0)
				break;
			list_set_category(fr,hit[2]);
			return;

		case 8:
			list_set_status(fr,0x200 + hit[2]-1);
			break;

		case 9:   /* add to address book */
			list_add_addresses(fr,0);
			break;

		case 10:  /* make distribution list */
			list_add_addresses(fr,1);
			break;

		case 11:
			copy_box = 0;
			/* drop through to next case */
		case 12:
			box = box_lookup_number(hit[2]-1);
			if((box < BOX_BIN) && (copy_box))
				list_selection_delete(fr,box,1);
			else
				list_selection_delete(fr,box,0);
			break;

		case 13:
			bounce_messages(fr,NULL);
			break;

		case 14:
			list_fetch_full(fr,0);
			break;
		}
		break;

	case 2:   /* Set status */
		list_status_menu_proc(fr,hit);
		break;

	case 3:   /* Sort */
		if(check_lock_lists_fr(0xffff,fr) != 0)
			break;
		display_called(hit[1]+1,fr,0);
		list_open_window(fr);
		dbox_setfield(fr->dbox_list,7,fr->display->name);
		break;

	case 4:   /* Make subset */
		subset_dialogue(fr,0);
		break;

	case 5:   /* Export */
		if(list_export_menu_proc(fr,hit)==0)
			return;
		break;

	case 6:   /* undelete */
		card_make_binned_menu(fr);
		wimp_get_point_info(&mouse);
		dbox_menu(wmenu_binned,card_binned_selected,&mouse);
		break;

	case 7:
		list_select_all_open(fr);
		blist_current_menu = NULL;
		return;

	}

	if(blist_current_menu == menu_list)
	{
		list_deselect(fr,blist_current_menu_line);
	}
}   /* end of list_menu_proc */


